3>&4-jest rozszerzeniem ksh93 obsługiwanym również przez bash i to jest skrót od 3>&4 4>&-, czyli 3 teraz wskazuje na to, gdzie 4 kiedyś, a 4 jest teraz zamknięty, więc to, na co wskazywał 4, teraz zmieniło się na 3.
Typowym zastosowaniem są przypadki, w których zduplikowano stdinlub stdoutzapisano kopię i chcesz ją przywrócić, na przykład:
Załóżmy, że chcesz przechwycić stderr polecenia (i tylko stderr), pozostawiając stdout sam w zmiennej.
Podstawienie polecenia var=$(cmd), tworzy potok. Koniec zapisu potoku staje się standardowym cmdwyjściem (deskryptor pliku 1), a drugi koniec jest odczytywany przez powłokę w celu wypełnienia zmiennej.
Teraz, jeśli chcesz stderr, aby przejść do zmiennej, można zrobić: var=$(cmd 2>&1). Teraz zarówno fd 1 (stdout), jak i 2 (stderr) idą do potoku (i ostatecznie do zmiennej), co stanowi tylko połowę tego, czego chcemy.
Jeśli tak zrobimy var=$(cmd 2>&1-)(skrót od var=$(cmd 2>&1 >&-), teraz tylko cmdstderr idzie do potoku, ale fd 1 jest zamknięty. Jeśli cmdspróbuje zapisać dane wyjściowe, które zwrócą się z EBADFbłędem, jeśli otworzy plik, otrzyma pierwszy wolny plik fd i zostanie mu przypisany otwarty plik, stdoutchyba że polecenie go chroni! Nie tego też chcemy.
Jeśli chcemy, aby standardowe wyjście cmdzostało pozostawione w spokoju, to znaczy wskazywać na ten sam zasób, na który wskazywał poza podstawieniem polecenia, to musimy jakoś wprowadzić ten zasób do podstawienia polecenia. W tym celu możemy wykonać kopię stdout poza podstawieniem polecenia, aby wziąć ją do środka.
{
var=$(cmd)
} 3>&1
Który jest czystszym sposobem na napisanie:
exec 3>&1
var=$(cmd)
exec 3>&-
(co ma również tę zaletę, że przywraca fd 3 zamiast go zamykać na końcu).
Następnie na {(lub exec 3>&1) i do }, zarówno fd 1, jak i 3 wskazują na ten sam zasób, na który początkowo wskazywał fd 1. fd 3 będzie również wskazywał na ten zasób w podstawieniu polecenia (podstawienie polecenia przekierowuje tylko fd 1, stdout). Tak więc powyżej cmdmamy dla fds 1, 2, 3:
- rura do var
- nietknięty
- to samo, co 1 wskazuje poza podstawieniem polecenia
Jeśli zmienimy to na:
{
var=$(cmd 2>&1 >&3)
} 3>&1-
Potem staje się:
- to samo, co 1 wskazuje poza podstawieniem polecenia
- rura do var
- to samo, co 1 wskazuje poza podstawieniem polecenia
Teraz mamy to, czego chcieliśmy: stderr idzie do rury, a stdout pozostaje nietknięty. Jednak wyciekamy z tego Fd 3 do cmd.
Podczas gdy polecenia (zgodnie z konwencją) zakładają, że fds 0 do 2 są otwarte i są standardowym wejściem, wyjściem i błędem, nie zakładają niczego z innych fds. Najprawdopodobniej pozostawiają ten fd 3 nietknięty. Jeśli będą potrzebować innego deskryptora pliku, zrobią to, open()/dup()/socket()...co zwróci pierwszy dostępny deskryptor pliku. Jeśli (podobnie jak skrypt powłoki exec 3>&1) muszą tego użyć fd, najpierw przypisują go do czegoś (i w tym procesie zasoby posiadane przez nasz fd 3 zostaną zwolnione przez ten proces).
Dobrą praktyką jest zamknięcie tego fd 3, ponieważ cmdnie korzysta z niego, ale nie jest to żadna wielka sprawa, jeśli zostawimy go przydzielonym przed zadzwonieniem cmd. Problemy mogą być takie: że cmd(i potencjalnie inne procesy, które się odradza) ma do dyspozycji o jeden mniej FD. Potencjalnie poważniejszym problemem jest to, że zasób wskazany przez fd może zostać zatrzymany przez proces spawnowany przez to cmdw tle. Może to budzić obawy, jeśli ten zasób jest potokiem lub innym kanałem komunikacji międzyprocesowej (tak jak podczas uruchamiania skryptu jako script_output=$(your-script)), ponieważ oznacza to, że proces odczytu z drugiego końca nigdy nie zobaczy końca pliku, dopóki proces w tle kończy się.
Tutaj lepiej napisać:
{
var=$(cmd 2>&1 >&3 3>&-)
} 3>&1
Które bashmożna skrócić do:
{
var=$(cmd 2>&1 >&3-)
} 3>&1
Podsumowując powody, dla których jest rzadko używany:
- to niestandardowy i po prostu cukier syntaktyczny. Musisz zrównoważyć oszczędność kilku naciśnięć klawiszy, czyniąc skrypt mniej przenośnym i mniej oczywistym dla osób nie przyzwyczajonych do tej niezwykłej funkcji.
- Konieczność zamknięcia oryginalnego pliku fd po powieleniu jest często pomijana, ponieważ przez większość czasu nie cierpimy z powodu konsekwencji, więc po prostu robimy to
>&3zamiast >&3-lub >&3 3>&-.
Dowód na to, że jest rzadko używany, jak się dowiedziałeś, jest fałszywy w bash . W bash compound-command 3>&4-lub any-builtin 3>&4-pozostawia fd 4 zamknięty nawet po compound-commandlub any-builtinwrócił. Patch naprawić problem jest teraz (19.02.2013) dostępne.