2013-08-29 27 views
7

kịch bản đầu tiên:Tại sao ống đặt lại thư mục làm việc hiện tại?

$ { mkdir dir; cd dir; pwd; } | cat; pwd; 
./dir 
. 

Thứ hai kịch bản:

$ { mkdir dir; cd dir; pwd; }; pwd; 
./dir 
./dir 

Tại sao | cat này có ảnh hưởng đến thư mục hiện hành? Và làm thế nào để giải quyết nó? Tôi cần kịch bản đầu tiên hoạt động chính xác như tập lệnh thứ hai. Tôi không muốn cat thay đổi thư mục hiện tại của tôi về ..

+2

http://stackoverflow.com/questions/5760640/left-side-of-pipe-is-the-subshell, 'cd' của bạn chạy trong một vỏ con. – Mat

+0

Trong tập lệnh thứ hai, nó cũng nằm trong subshell – yegor256

+6

@ yegor256 Không, trong lần thứ hai nó ** không ** trong một vỏ bọc con. – devnull

Trả lời

11

Trích dẫn từ manual:

Mỗi lệnh trong một đường ống được thực hiện trong chính vỏ bọc con của nó (xem Command Execution Environment).

Xem thêm Grouping Commands:

{}

{ list; } 

Đặt một danh sách các lệnh giữa dấu ngoặc nhọn gây ra danh sách để được thực hiện trong bối cảnh shell hiện hành. Không có subshell nào được tạo.

0

Đường ống không đặt lại thư mục làm việc hiện tại. Các đường ống tạo ra subshells, trong bash một cho mỗi bên của đường ống. Subshell cũng không đặt lại thư mục làm việc hiện tại. Subshell chứa các thay đổi đối với môi trường trong chính nó và không truyền bá chúng đến shell cha.

Trong kịch bản đầu tiên của bạn:

$ { mkdir dir; cd dir; pwd; } | cat; pwd; 

Các cd được thực hiện bên trong subshell trái của ống. Thư mục làm việc chỉ được thay đổi trong subshell đó. Thư mục làm việc của shell cha không bị thay đổi. Các pwd đầu tiên được thực hiện trong cùng một subshell là cd vì vậy nó đọc thư mục làm việc thay đổi. pwd ở cuối được thực thi trong shell cha để nó đọc thư mục làm việc của shell cha không bị thay đổi.

Trong kịch bản thứ hai của bạn:

$ { mkdir dir; cd dir; pwd; }; pwd; 

Không có subshell tạo. Các dấu ngoặc nhọn không tạo ra một subshell. cd được thực hiện trong trình bao gốc và cả hai pwd s đọc thư mục làm việc đã thay đổi.

Để biết thêm thông tin và giải thích đọc bash mịn tay về:

1

Hành vi của lệnh đường dẫn liên quan đến biến được thực hiện xác định.

Bạn tình cờ sử dụng bash để chọn đặt mọi thành phần trong nền vỏ sao cho hiệu ứng cd bị mất trong vỏ chính.

Nếu bạn đã chạy ksh, hãy chọn giữ nguyên tố cuối cùng của đường ống trong vỏ hiện tại và nếu bạn đã đặt cd trong tuyên bố cuối cùng, hành vi sẽ là hành vi bạn mong đợi.

$ bash 
$ { mkdir -p dir; cd dir; pwd; } | { cat; mkdir -p dir; cd dir; pwd ; } ; pwd; 
/tmp/dir 
/tmp/dir 
/tmp 
$ ksh 
$ { mkdir -p dir; cd dir; pwd; } | { cat; mkdir -p dir; cd dir; pwd ; } ; pwd; 
/tmp/dir 
/tmp/dir 
/tmp/dir 
2

Nó không phải là các đường ống đi trở lại vào thư mục, đó là bạn đã thực hiện lệnh đầu tiên (trước dấu chấm phẩy) chỉ áp dụng cho các lệnh cat. Về cơ bản, bạn đang tạo đường ống cho đầu ra của quy trình con của các số mkdircdpwd chuyển đến quy trình cat.

Ví dụ: { mkdir dir; cd dir; pwd; } | cat; pwd;

Đầu tiên mở rộng thành hai quá trình: 1) { mkdir dir; cd dir; pwd; } | cat; và 2) pwd

Quá trình đầu tiên mở rộng thành hai quy trình, { mkdir dir; cd dir; pwd; } mà sau đó gửi stdout của nó để stdin của cat. Khi người đầu tiên của các quá trình hai kết thúc và stdout được thu thập, thoát trình con của nó và nó cũng giống như cd bao giờ xảy ra vì cd chỉ ảnh hưởng đến các thư mục của quá trình đó đang chạy vào. pwd bao giờ thực sự đã thay đổi $PWD, nó chỉ in được cung cấp trên stdin.

Để giải quyết vấn đề này (giả sử tôi hiểu những gì bạn đang cố gắng để làm) tôi sẽ thay đổi điều này để:

{ mkdir dir; cd dir; pwd; }; pwd; cd -

+1

Thay thế được đề xuất của bạn sẽ không hoạt động vì 'cat' sẽ đợi vô thời hạn cho' EOF' trên 'stdin', nó sẽ không nhận được . – sanmiguel

+2

Ngoài ra, lời giải thích của bạn không hoàn toàn chính xác - LHS của ** pipe ** được thực hiện trong một subshell. Nếu không, '{mkdir dir; cd dir; pwd; } | {con mèo; pwd;} 'sẽ dẫn đến cùng' $ PWD' cho cả hai lệnh gọi 'pwd'. – sanmiguel

+0

Cả hai đều đúng. sanmiguel đặc biệt như vậy. Xin lỗi về điều đó :) – manchicken

2

Khi bạn chạy:

{ mkdir -p dir; cd dir; pwd; } | cat; pwd 

HOẶC

{ mkdir -p dir; cd dir; pwd; } | date 

HOẶC

{ mkdir -p dir; cd dir; pwd; } | ls 

Bạn đang chạy nhóm các lệnh trên LHS của ống trong một tiểu vỏ và do đó change dirlà không được phản ánh trong vỏ hiện sau khi cả hai lệnh (LHS và RHS) hoàn tất.

Tuy nhiên khi bạn chạy:

{ mkdir -p dir; cd dir; pwd; }; pwd; 

Không có ống ở giữa do đó tất cả các lệnh bên trong dấu ngoặc nhọn và pwd ngoài xoăn cú đúp chạy trong vỏ hiện bản thân do đó bạn sẽ có được thay đổi thư mục.

PS: Cũng lưu ý rằng dòng này:

(mkdir -p dir; cd dir; pwd;) 

Cũng sẽ không thay đổi thư mục hiện hành trong vỏ hiện tại vì các lệnh bên trong dấu ngoặc vuông thực hiện trong một vỏ tiểu trong khi dấu ngoặc nhọn được chỉ được sử dụng cho nhóm.

+2

Nó sẽ tốt hơn/đơn giản hơn để sử dụng 'mkdir -p dir' mà không thất bại nếu thư mục tồn tại chứ không phải là' rmdir dir' mà thất bại nếu thư mục không tồn tại? –

+0

@JonathanLeffler: Đồng ý, đã chỉnh sửa. – anubhava

0

Mặc dù hướng dẫn sử dụng cho biết:

{ list; } 

Placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created. 

Đặt nó trên một đường ống dẫn vẫn sẽ đặt nó trên một subshell.

{ list; } | something 

Kể từ

Each command in a pipeline is executed in its own subshell (see Command Execution Environment). 

Các lệnh trong { } bản thân sẽ không được đặt trên một subshell nhưng bối cảnh cao hơn của nó bản thân sẽ là lý do tại sao nó vẫn sẽ là như nhau.

Là một thử nghiệm chạy (){ } sẽ chỉ được giống nhau:

# echo "$BASHPID" 
6582 
# (ps -p "$BASHPID" -o ppid=) | cat 
6582 
# { ps -p "$BASHPID" -o ppid=; } | cat 
6582 

Cả gửi quá trình cha mẹ như vỏ gọi.

Các vấn đề liên quan