2013-08-29 11 views
7

Pierwszy scenariusz:Dlaczego rura resetuje bieżący katalog roboczy?

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

Drugi scenariusz:

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

Dlaczego | cat ma wpływ na bieżącym katalogu? I jak go rozwiązać? Potrzebuję pierwszego skryptu, aby działał dokładnie tak samo jak drugi. Nie chcę, aby cat zmienił mój bieżący katalog z powrotem na ..

+2

http://stackoverflow.com/questions/5760640/left-side-of-pipe-is-subshell, twoje 'cd' działa w podpowłoce. – Mat

+0

W drugim skrypcie jest również w podpowierzchni – yegor256

+6

@ yegor256 Nie, w drugim jest ** nie ** w podpowłoce. – devnull

Odpowiedz

11

Cytowanie od manual:

Każde polecenie w potoku jest wykonywane w osobnej podpowłoce (patrz Command Execution Environment).

zobaczyć również Grouping Commands:

{}

{ list; } 

Umieszczenie listę poleceń między klamrami powoduje, że lista będzie realizowane w bieżącym kontekście powłoki. Nie utworzono podpowłoki.

0

Rura nie resetuje bieżącego katalogu roboczego. Rura tworzy podpowłoki, w bashie po każdej stronie rury. Podpowiedź również nie resetuje bieżącego katalogu roboczego. Podpatka zawiera zmiany w samym środowisku i nie propaguje ich do powłoki nadrzędnej.

W pierwszym scenariuszu:

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

cd jest wykonywany wewnątrz lewej podpowłoce rury. Katalog roboczy zmienia się tylko w tej podpowłoce. Katalog roboczy powłoki głównej nie jest zmieniany. Pierwszy kod pwd jest wykonywany w tej samej podpowłoce co cd, dzięki czemu odczytuje zmieniony katalog roboczy. pwd na końcu jest wykonywany w powłoce nadrzędnej, więc odczytuje katalog roboczy powłoki głównej, który nie został zmieniony.

W swoim drugim skrypcie:

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

Nie ma powłoki w tle tworzone. Kręcone nawiasy klamrowe nie tworzą podpowłok. Numer cd jest wykonywany w powłoce rodzica i oba czytują zmieniony katalog roboczy.

Aby uzyskać więcej informacji i wyjaśnień przeczytać drobnym bash ręcznego about:

1

Zachowanie komend rurociągu w odniesieniu do zmiennych jest zdefiniowane przez implementację.

Zdarza się, że używasz bash, które decydują się umieścić każdy komponent w powłoce w tle, aby efekt cd został utracony w głównej powłoce.

Jeśli został uruchomiony ksh który wybrać, aby utrzymać ostatni element rurociągu w bieżącej powłoki i należy wyprowadzić cd w ostatnim oświadczeniu zachowanie byłby jeden można oczekiwać.

$ 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

To nie tak, że rura wraca do katalogu, jest to, że po dokonaniu pierwszego polecenia (przed średnikiem) zastosowanie tylko do komendy cat. W gruncie rzeczy przetwarzasz wynik podprocesu procesu mkdir i cd i pwd w procesie cat.

Na przykład { mkdir dir; cd dir; pwd; } | cat; pwd;

najpierw rozszerza się dwoma sposobami: 1) { mkdir dir; cd dir; pwd; } | cat; i 2) pwd

Pierwszy proces rozszerza się dwa procesy, { mkdir dir; cd dir; pwd; } który następnie wysyła stdout do stdin z cat. Gdy pierwszy z tych dwóch procesów się zakończy i zostanie pobrane stdout, jego podproces kończy działanie i nie ma to miejsca, ponieważ cd nigdy nie miało miejsca, ponieważ cd wpływa tylko na katalog procesu, w którym działał. pwd nigdy się nie zmienił $PWD, został tylko wydrukowany to, co zostało dostarczone pod numerem stdin.

Aby rozwiązać ten problem (zakładając, że rozumiem, co staramy się robić) Chciałbym zmienić to:

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

+1

Twoja sugerowana zamiana nie zadziała, ponieważ 'cat' będzie czekać bezterminowo na' EOF' na swoim 'stdin', którego nie otrzyma . – sanmiguel

+2

Również twoje wyjaśnienie nie jest całkiem poprawne - LHS z ** pipe ** jest wykonywane w podpowłoce. W przeciwnym razie '{mkdir dir; cd dir; pwd; } | {kot; pwd;} 'spowodowałoby to samo' $ PWD' dla obu wywołań 'pwd'. – sanmiguel

+0

Oboje macie rację. w szczególności jest to sanmiguel. Przepraszam za to :) – manchicken

2

Po uruchomieniu:

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

LUB

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

OR

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

Uruchomiony grupy rozkazów na LHS rury w podpowłoce, a tym samym change dirnie jest widoczne w tym powłoki po obu komend (LHS i RHS) kompletne.

Jednak po uruchomieniu:

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

Nie ma rury pomiędzy stąd wszystkich poleceń wewnątrz nawiasów klamrowych i pwd poza nawias klamrowy biegu w bieżącej powłoki samego stąd dostać zmieniony katalog.

PS: Należy również pamiętać, że ta linia:

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

również nie zmienia katalog bieżący w bieżącej powłoki ponieważ polecenia w nawiasach kwadratowych wykonać w powłoce sub natomiast nawiasy klamrowe służą tylko do grupowania.

+2

Czy lepiej/prościej byłoby użyć 'mkdir -p dir', który nie zawodzi, jeśli katalog istnieje, a nie' rmdir dir', który zawodzi, jeśli katalog nie istnieje? –

+0

@ JonathanLeffler: Uzgodnione, zredagowane. – anubhava

0

Chociaż podręcznikach mówi:

{ list; } 

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

Umieszczenie go na rurociągu nadal umieścić go na podpowłoce.

{ list; } | something 

Od

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

Komendy w { } sama nie będzie umieszczony na podpowłoce ale wyższy kontekst sama byłaby to dlatego, że to wciąż taka sama.

Jako test uruchomiony () i { } będzie tylko taka sama:

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

Zarówno proces nadrzędny wysyła jako powłoki wywołującego.

Powiązane problemy