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.
{ var=$(cmd 2>&1 >&3) ; } 3>&1-
Czy to nie literówka w zamykaniu 1?$(...)
).{...}
, fd 3 wskazuje na to, co wskazało fd 1, a fd 1 jest zamykane, a następnie po wejściu$(...)
, fd 1 jest ustawiane na rurze, która się karmi$var
, następnie nacmd
2 również na to, a następnie 1 na jakie 3 punkty do, to jest zewnętrzne 1. Fakt, że 1 pozostaje zamknięty, jest błędem w bashu, zgłoszę to. ksh93, skąd pochodzi ta funkcja, nie ma tego błędu.Oznacza to, że wskazuje on to samo miejsce, co inny deskryptor pliku. Trzeba to zrobić bardzo rzadko, oprócz oczywistej oddzielnego postępowania standardowego deskryptora błędu (
stderr
,fd 2
,/dev/stderr -> /proc/self/fd/2
). Może się przydać w niektórych skomplikowanych przypadkach.Przewodnik Advanced Bash Scripting zawiera następujący przykład poziomu dziennika i ten fragment kodu:
W Source Mage's Sorcery używamy go na przykład do rozróżnienia różnych wyników z tego samego bloku kodu:
Do celów logowania dołączono dodatkowe podstawianie procesów (VOYEUR decyduje, czy dane powinny być wyświetlane na ekranie, czy tylko rejestrować), ale niektóre komunikaty muszą być zawsze prezentowane. Aby to osiągnąć, drukujemy je do deskryptora pliku 3, a następnie obsługujemy go specjalnie.
źródło
W Uniksie pliki są obsługiwane przez deskryptory plików (małe liczby całkowite, na przykład standardowe wejście to 0, standardowe wyjście to 1, standardowy błąd to 2; gdy otwierasz inne pliki, zwykle otrzymują najmniejszy nieużywany deskryptor). Więc jeśli znasz wewnętrzne funkcje programu i chcesz wysłać dane wyjściowe, które trafiają do deskryptora pliku 5 do standardowego wyjścia, możesz przenieść deskryptor 5 do 1. Stąd
2> errors
pochodzi, a konstrukcje lubią2>&1
kopiować błędy do strumień wyjściowy.Tak więc prawie nigdy nie był używany (niejasno pamiętam, że użyłem go raz lub dwa razy w gniewie w ciągu moich ponad 25 lat prawie wyłącznego używania Uniksa), ale kiedy jest to absolutnie konieczne.
źródło
5>&1
wysyła 5 do dokąd 1 zmierza, to co dokładnie robi1>&5-
oprócz zamykania 5?