Próbuję zrozumieć, jak dokładnie Bash traktuje następujący wiersz:
$(< "$FILE")
Według strony podręcznika Bash jest to równoważne z:
$(cat "$FILE")
i mogę podążać za rozumowaniem tej drugiej linii. Bash wykonuje rozszerzanie zmiennej $FILE
, wprowadza podstawienie polecenia, przekazuje wartość $FILE
do cat
, cat przekazuje zawartość $FILE
standardowego wyjścia, podstawianie polecenia kończy się przez zastąpienie całej linii standardowym wyjściem wynikającym z polecenia wewnątrz, a Bash próbuje wykonać to jak proste polecenie.
Jednak w pierwszym wierszu, o którym wspomniałem powyżej, rozumiem to jako: Bash wykonuje podstawienie zmiennej $FILE
, Bash otwiera się $FILE
do odczytu na standardowym wejściu, w jakiś sposób standardowe wejście jest kopiowane na standardowe wyjście , podstawianie poleceń kończy się, a Bash próbuje wykonać wynikowy standard wynik.
Czy ktoś może mi wyjaśnić, w jaki sposób zawartość $FILE
zmienia się ze standardowego na standardowe?
źródło
bash
zinterpretuje to jakocat filename
”, czy masz na myśli, że to zachowanie jest specyficzne dla zastępowania poleceń? Bo jeśli uruchomię< filename
się sam, bash go nie wykręci. Nic nie wyświetli i nie wróci do monitu.cat < filename
docat filename
której jestem przeciwny i może powrócić.|
tworzy potok między dwoma podprocesami (lub, w przypadku niektórych powłok, od podprocesu do standardowego wejścia powłoki). Operator powłoki$(…)
tworzy potok z podprocesu do samej powłoki (nie do standardowego wejścia). Operator powłoki<
nie angażuje potoku, jedynie otwiera plik i przenosi deskryptor pliku na standardowe wejście.< file
nie jest taki sam jakcat < file
(z wyjątkiem tego,zsh
gdzie jest$READNULLCMD < file
).< file
jest idealnie POSIX i po prostu otwiera sięfile
do czytania, a następnie nic nie robi (więcfile
jest od razu blisko). To$(< file)
czy`< file`
jest to specjalny operatorksh
,zsh
ibash
(a zachowanie pozostaje nieokreślona w POSIX). Zobacz moją odpowiedź, aby poznać szczegóły.$(cmd1) $(cmd2)
zwykle będzie taki sam jak$(cmd1; cmd2)
. Ale spójrz na przypadek, w którymcmd2
jest< file
. Jeśli mówimy$(cmd1; < file)
, plik nie jest odczytywany, ale z$(cmd1) $(< file)
nim jest. Nie można więc powiedzieć, że$(< file)
jest to zwykły przypadek$(command)
polecenia< file
.$(< …)
jest szczególnym przypadkiem zastępowania poleceń, a nie normalnym użyciem przekierowania.$(<file)
(działa również z`<file`
) jest specjalnym operatorem powłoki Korna skopiowanym przezzsh
ibash
. Wygląda bardzo podobnie do zastępowania poleceń, ale tak naprawdę nie jest.W powłokach POSIX proste polecenie to:
Wszystkie części są opcjonalne, możesz mieć tylko przekierowania, tylko polecenia, tylko przypisanie lub kombinacje.
Jeśli istnieją przekierowania, ale nie ma polecenia, przekierowania są wykonywane (więc
> file
otworzy się i obcinafile
), ale wtedy nic się nie dzieje. WięcOtwiera się
file
do odczytu, ale wtedy nic się nie dzieje, ponieważ nie ma polecenia. Więc tofile
jest zamknięte i to wszystko. Gdyby$(< file)
proste zastąpienie polecenia , rozwinęłoby się do zera.W specyfikacji POSIX , w
$(script)
, jeśliscript
składa się tylko z przekierowań, daje nieokreślone wyniki . To pozwala na to specjalne zachowanie skorupy Korna.W ksh (tutaj testowane z
ksh93u+
), jeśli skrypt składa się z jednego i tylko jednego prostego polecenia (chociaż komentarze są dozwolone przed i po), który składa się tylko z przekierowań (bez polecenia, bez przypisania) i jeśli pierwszym przekierowaniem jest stdin (fd 0) tylko wejście (<
,<<
i<<<
) przekierowania sposób:$(< file)
$(0< file)
$(<&3)
(również w$(0>&3)
rzeczywistości, ponieważ w rzeczywistości jest to ten sam operator)$(< file > foo 2> $(whatever))
ale nie:
$(> foo < file)
$(0<> file)
$(< file; sleep 1)
$(< file; < file2)
następnie
<&3
), minus końcowe znaki nowego wiersza.jakbyś za
$(cat < file)
wyjątkiem tegocat
$(<${file=foo.txt})
lub$(<file$((++n)))
)W
zsh
, to samo z wyjątkiem tego, że szczególną zachowanie jest wyzwalany tylko wtedy, gdy istnieje tylko jeden plik wejściowy przekierowania (<file
lub0< file
nie<&3
,<<<here
,< a < b
...)Jednak z wyjątkiem emulacji innych powłok, w:
oznacza to, że istnieją tylko przekierowania wejściowe bez poleceń, poza podstawianiem poleceń,
zsh
uruchamia$READNULLCMD
(domyślnie pager), a gdy są przekierowania wejściowe i wyjściowe,$NULLCMD
(cat
domyślnie), więc nawet jeśli$(<&3)
nie jest to rozpoznawane jako specjalne operator nadal będzie działał tak jak w tymksh
przypadku, wywołując do tego pager (ten pager zachowuje się tak, jakbycat
jego standardowe wyjście było potokiem).Jednak podczas gdy
ksh
„s$(< a < b)
rozwinie się do treścia
, wzsh
, rozszerza się do treścia
ib
(lub tylkob
wtedy, gdymultios
opcja jest wyłączona),$(< a > b)
by skopiowaća
dob
i rozwinąć do niczego, itd.bash
ma podobny operator, ale z kilkoma różnicami:komentarze są dozwolone przed, ale nie po:
działa, ale:
rozwija się do zera.
jak się
zsh
tylko jeden plik stdin przekierowania, choć nie ma spaść z powrotem do$READNULLCMD
, tak$(<&3)
,$(< a < b)
nie wykonywać przekierowań ale poszerzyć do niczego.bash
że nie wywołujecat
, nadal rozwidla proces, który przekazuje zawartość pliku przez potok, dzięki czemu jest mniej optymalizacyjny niż w innych powłokach. Jest to w istocie niczym$(cat < file)
gdziecat
byłaby wbudowanecat
.$(<${file=foo.txt})
wyżej wspomnianym na przykład$file
przypisanie jest tracone później).W
bash
,IFS= read -rd '' var < file
(działa także wzsh
) jest bardziej skuteczny sposób, aby odczytać zawartość tekstu pliku do zmiennej. Ma także tę zaletę, że zachowuje końcowe znaki nowego wiersza. Zobacz także$mapfile[file]
wzsh
(wzsh/mapfile
module i tylko dla zwykłych plików), który działa również z plikami binarnymi.Zauważ, że warianty oparte na pdksh
ksh
mają kilka odmian w porównaniu do ksh93. Interesujące wmksh
(jednej z tych powłok pochodzących z pdksh), wjest zoptymalizowany pod tym względem, że zawartość tego dokumentu (bez znaków końcowych) jest rozszerzana bez użycia pliku tymczasowego lub potoku, jak ma to miejsce w przypadku dokumentów tutaj, co czyni go efektywną składnią wielowierszowego cytowania.
Aby być przenośnym dla wszystkich wersji
ksh
,zsh
abash
najlepiej ograniczyć się tylko do$(<file)
unikania komentarzy i pamiętać, że modyfikacje dokonanych w nich zmiennych mogą, ale nie muszą zostać zachowane.źródło
$(<)
jest operatorem nazw plików? Czy<
w$(<)
operatora przekierowania, czy nie operator na własną rękę, i muszą być częścią całego operatora$(<)
?$(<file)
ma na celu rozszerzenie do treścifile
w podobny sposób$(cat < file)
. To, jak to się robi, różni się w zależności od powłoki, co opisano szczegółowo w odpowiedzi. Jeśli chcesz, możesz powiedzieć, że jest to specjalny operator, który jest uruchamiany, gdy coś, co wygląda jak podstawienie polecenia (składniowo) zawiera coś, co wygląda jak pojedyncze przekierowanie standardowego wejścia (składniowo), ale znowu z zastrzeżeniami i odmianami zależnymi od powłoki wymienionymi tutaj .n<&m
in>&m
zrobić to samo? Nie wiedziałem o tym, ale chyba nie jest to zaskakujące.dup(m, n)
. Widzę pewne dowody na użycie ksh86 przy użyciu stdio i niektórefdopen(fd, "r" or "w")
, więc mogło to mieć znaczenie. Ale używanie stdio w powłoce nie ma większego sensu, więc nie oczekuję, że znajdziesz jakąkolwiek nowoczesną powłokę, w której to coś zmieni. Jedną różnicą jest to, że>&n
jestdup(n, 1)
(skrót od1>&n
), podczas gdy<&n
jestdup(n, 0)
(skrót od0<&n
).dup2()
;dup()
pobiera tylko jeden argument i, na przykładopen()
, używa najniższego dostępnego deskryptora pliku. (Dzisiaj dowiedziałem się, że jest jakaśdup3()
funkcja ).Ponieważ
bash
robi to wewnętrznie dla ciebie, rozwinął nazwę pliku i przechowuje plik na standardowe wyjście, tak jakbyś to zrobił$(cat < filename)
. Jest to funkcja bash, być może trzeba zajrzeć dobash
kodu źródłowego, aby dokładnie wiedzieć, jak to działa.Oto funkcja do obsługi tej funkcji (z
bash
kodu źródłowego, plikubuiltins/evalstring.c
):Uwaga, która
$(<filename)
nie jest dokładnie równoważna z$(cat filename)
; to drugie zawiedzie, jeśli nazwa pliku zaczyna się od myślnika-
.$(<filename)
był pierwotnie odksh
i dodano dobash
zBash-2.02
.źródło
cat filename
zawiedzie, jeśli nazwa pliku zaczyna się od myślnika, ponieważ cat akceptuje opcje. Możesz obejść ten problem w większości nowoczesnych systemówcat -- filename
.Pomyśl o zastąpieniu polecenia jak o zwykłym uruchomieniu polecenia i zrzuceniu danych wyjściowych w punkcie, w którym uruchamiasz polecenie.
foo=$(echo "bar")
ustawi wartość zmiennej$foo
nabar
; wyjście poleceniaecho bar
.Zmiana polecenia
źródło
$(< file)
i nie potrzebuje samouczka na temat ogólnego przypadku. Jeśli mówisz, że$(< file)
to zwykły przypadek$(command)
z rozkazem< file
, to mówisz to samo, co mówi Adam Katz , i oboje się mylicie.