Weźmy dwie linie poniżej, które dają nam dwa różne wyniki.
p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ | pwd) ; echo $p
Czym się różnią?
|uruchamiania w podpowłokach.
Weźmy dwie linie poniżej, które dają nam dwa różne wyniki.
p=$(cd ~ && pwd) ; echo $p
p=$(cd ~ | pwd) ; echo $p
Czym się różnią?
|uruchamiania w podpowłokach.
Odpowiedzi:
W p=$(cd ~ && pwd):
Podstawienie polecenia $(), działa w podpowłoce
cd ~zmienia katalog na ~(twój dom), jeśli się cdpowiedzie ( &&), następnie pwdwypisuje nazwę katalogu na STDOUT, stąd zapisany ciąg pbędzie katalogiem domowym np./home/foobar
W p=$(cd ~ | pwd):
Ponownie $()pojawia się podpowłoka
Komendy po obu stronach |uruchamiania w odpowiednich podpowłokach (i obie rozpoczynają się w tym samym czasie)
tak cd ~odbywa się w podpowłoce oraz pwdw oddzielnym podpowłoce
więc dostaniesz tylko STDOUT, pwdtj. z miejsca, w którym uruchomisz polecenie, może to być dowolny katalog, jak możesz sobie wyobrazić, a zatem pbędzie on zawierał nazwę katalogu, z którego polecenie jest wywoływane, a nie katalog domowy
cd ~nie generuje żadnych danych wyjściowych i pwdnie odczytuje żadnych danych wejściowych.
(cd ~);p=$(pwd)prawda?
Podstawowym problemem jest sposób, w jaki operatorzy &&i |łączą oba polecenia.
&&Łączy polecenia za pośrednictwem kodu wyjścia. |Łączy dwa polecenia za pomocą deskryptorów (stdin, stdout).
Uprośćmy najpierw. Możemy usunąć zadanie i napisać:
echo $(cd ~ && pwd)
echo $(cd ~ | pwd)
Możemy nawet usunąć podpowłokę wykonania polecenia, aby to przeanalizować:
$ cd ~ && pwd
$ cd ~ | pwd
Jeśli zmienimy monit, aby pokazać katalog, w którym wykonywane są polecenia, coś w PS1='\w\$ 'tym stylu:
/tmp/user$ cd ~ && pwd
/home/user
~$
cd ~zmieniło „obecny katalog” na domową wersję użytkownika, który wykonuje polecenie ( /home/user).pwdna, ~jak pokazuje monit ~$.Jeśli z jakiegoś powodu zmiana katalogu nie powiodła się (kod wyjścia nie wynosi 0) (katalog nie istnieje, blok uprawnień odczytu katalogu) następna komenda nie zostanie wykonana.
Przykład:
/tmp/user$ false && pwd
/tmp/user$ _
Kod wyjścia 1 z falseuniemożliwia wykonanie następnego polecenia.
Zatem kod wyjścia „polecenia 1” wpływa na „polecenie 2”.
Teraz efekty całego polecenia:
/tmp/user$ echo $(cd ~ && pwd)
/home/user
/tmp/user$ _
Katalog został zmieniony, ale wewnątrz podpowłoki $(…)zmieniony katalog jest drukowany /home/user, ale jest natychmiast odrzucany po zamknięciu podpowłoki. Pwd wraca do katalogu początkowego ( /tmp/user).
Oto co się dzieje:
/tmp/user$ cd ~ | pwd
/tmp/user
/tmp/user$ _
Metaznak |(nie prawdziwy operator) sygnalizuje powłoce utworzenie „potoku”, (w skrócie) każde polecenie po każdej stronie potoku ( |) jest ustawione wewnątrz każdej własnej podpowłoki, najpierw po prawej stronie polecenie, a następnie lewy. Deskryptor pliku wejściowego ( /dev/stdin) prawego polecenia jest połączony z deskryptorem wyjściowym ( /dev/stdout), a następnie oba polecenia są uruchamiane i pozostawione do interakcji. Lewe polecenie ( cd -) nie ma danych wyjściowych, a także prawe polecenie ( pwd) nie przyjmuje danych wejściowych. Tak więc każdy działa niezależnie w każdej własnej podpowłoce.
cd ~Zmienia pwd jednej powłoce.pwdDrukuje (PWD) całkowicie niezależne od innych pod-obudowy.Zmiany w każdej powłoce są odrzucane, gdy rura się kończy, zewnętrzna podpowłoka nie zmieniła PWD.
Dlatego te dwa polecenia są połączone tylko przez „deskryptory plików”.
W tym przypadku nic nie jest wysyłane i nic nie jest czytane.
Całe polecenie:
$ echo "$(cd ~ | pwd)"
Po prostu wydrukuje katalog, w którym polecenie zostało wykonane.
Nie jestem pewien, czy miałeś na myśli „|” lub „||” w twoim drugim przypadku.
„|” w powłoce potokuje wyjście jednego polecenia na wejście innego - częstym przypadkiem użycia jest coś takiego:
curl http://abcd.com/efgh | grep ijkl
np. uruchom polecenie i użyj innego polecenia, aby przetworzyć wynik polecenia.
W podanym przykładzie jest to dość nonsensowne, ponieważ „cd” zwykle nie generuje żadnych danych wyjściowych, a „pwd” nie oczekuje żadnych danych wejściowych.
„&&” i „||” są jednak poleceniami partnerów. Są zaprojektowane tak, aby można było z nich korzystać w taki sam sposób, jak operatory logiczne „i” i „lub” w większości języków. Jednak przeprowadzane optymalizacje nadają im określone zachowanie, które jest paradygmatem programowania powłoki.
Aby określić wynik operacji logicznej „i”, należy ocenić drugi warunek tylko wtedy, gdy pierwszy warunek się powiedzie - jeśli pierwszy warunek się nie powiedzie, ogólny wynik zawsze będzie fałszywy.
Aby określić wynik operacji logicznej „lub”, musisz ocenić drugi warunek tylko wtedy, gdy pierwszy warunek się nie powiedzie - jeśli pierwszy warunek się powiedzie, ogólny wynik zawsze będzie prawdziwy.
Tak więc w powłoce, jeśli ją wykonasz, command1 && command2 command2zostanie wykonany dopiero po command1zakończeniu i zwróceniu pomyślnego kodu wynikowego. Jeśli masz, command1 || command2 command2zostanie wykonany po command1zakończeniu, jeśli command1zwróci kod błędu.
Innym powszechnym paradygmatem jest command1komenda testowa - generuje ona pojedynczą instrukcję if / then - na przykład:
[ "$VAR" = "" ] && VAR="Value if empty"
Jest (długo rozwiniętym) sposobem przypisywania wartości zmiennej, jeśli jest ona obecnie pusta.
Istnieje wiele przykładów zastosowania tego procesu w innym miejscu na Stack Exchange