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 stdin
lub stdout
zapisano 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 cmd
wyjś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 cmd
stderr idzie do potoku, ale fd 1 jest zamknięty. Jeśli cmd
spróbuje zapisać dane wyjściowe, które zwrócą się z EBADF
błędem, jeśli otworzy plik, otrzyma pierwszy wolny plik fd i zostanie mu przypisany otwarty plik, stdout
chyba że polecenie go chroni! Nie tego też chcemy.
Jeśli chcemy, aby standardowe wyjście cmd
został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 cmd
mamy 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ż cmd
nie 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 cmd
w 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 bash
moż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
>&3
zamiast >&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-command
lub any-builtin
wrócił. Patch naprawić problem jest teraz (19.02.2013) dostępne.