Gdy przekierowujesz listę poleceń zawierającą przekierowanie exec, exec> / dev / null nie wydaje się być później stosowane, na przykład z:
{ exec >/dev/null; } >/dev/null; echo "Hi"
Drukowane jest „Cześć”.
Miałem wrażenie, że {}
lista poleceń nie jest uważana za podpowłokę, chyba że jest ona częścią potoku, więc exec >/dev/null
powinna być nadal stosowana w bieżącym środowisku powłoki.
Teraz, jeśli zmienisz to na:
{ exec >/dev/null; } 2>/dev/null; echo "Hi"
wyniki nie są zgodne z oczekiwaniami; deskryptor pliku 1 pozostaje wskazany na / dev / null również dla przyszłych poleceń. Pokazuje to ponowne uruchomienie:
{ exec >/dev/null; } >/dev/null; echo "Hi"
co nie da wyniku.
Próbowałem napisać scenariusz i wyszukać go, ale wciąż nie jestem pewien, co dokładnie się tutaj dzieje.
Co dzieje się z deskryptorem pliku STDOUT w każdym punkcie tego skryptu?
EDYCJA: Dodanie mojego wyjścia strace:
read(255, "#!/usr/bin/env bash\n{ exec 1>/de"..., 65) = 65
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
close(10) = 0
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, TCGETS, 0x7ffee027ef90) = -1 ENOTTY (Inappropriate ioctl for device)
write(1, "hi\n", 3) = 3
źródło
close(10)
. Czy możesz również opublikować całą zawartość skryptu, na którym uruchomiłeś strace?;
po}
, co zmienia znaczenie,> /dev/null
by nie stosować się do listy złożonej{}
.Odpowiedzi:
Podążajmy
krok po kroku.
Istnieją dwa polecenia:
za.
{ exec >/dev/null; } >/dev/null
, śledzony przezb.
echo "Hi"
Powłoka wykonuje najpierw polecenie (a), a następnie polecenie (b).
Wykonanie
{ exec >/dev/null; } >/dev/null
przebiega następująco:za. Po pierwsze, powłoka wykonuje przekierowanie
>/dev/null
i pamięta o cofnięciu go po zakończeniu polecenia .b. Następnie wykonywana jest powłoka
{ exec >/dev/null; }
.do. Na koniec powłoka przełącza standardowe wyjście z powrotem tam, gdzie było. (Jest to ten sam mechanizm, co
ls -lR /usr/share/fonts >~/FontList.txt
przekierowania wewnętrzne - dokonywane są tylko na czas trwania polecenia, do którego należą).Po wykonaniu pierwszego polecenia powłoka jest wykonywana
echo "Hi"
. Standardowe wyjście jest tam, gdzie było przed pierwszym poleceniem.źródło
Aby nie używać podpowłoki lub podprocesu, gdy dane wyjściowe z listy złożonej
{}
są przesyłane potokowo>
, powłoka zapisuje deskryptor STDOUT przed uruchomieniem listy złożonej i przywraca ją później. Zatem naexec >
liście złożonej nie przenosi efektu poza punkt, w którym stary deskryptor jest przywracany jako STDOUT.Rzućmy okiem na odpowiednią część
strace bash -c '{ exec >/dev/null; } >/dev/null; echo hi' 2>&1 | cat -n
:Możesz zobaczyć, jak w linii 134 deskryptor
1
(STDOUT
) jest kopiowany na inny deskryptor z co najmniej indeksem10
(to właśnieF_DUPFD
robi; zwraca najniższy dostępny deskryptor zaczynając od podanej liczby po powieleniu na tym deskryptorze). Zobacz także, jak w linii 137 wynikopen("/dev/null")
(deskryptor3
) jest kopiowany do descriptor1
(STDOUT
). Wreszcie, on-line147
starySTDOUT
zapisany w deskryptorze10
jest kopiowany z powrotem do descriptor1
(STDOUT
). Efektem netto jest izolacja zmianySTDOUT
na linię144
(która odpowiada wewnętrznejexec >/dev/null
).źródło
exec
.Różnica między
{ exec >/dev/null; } >/dev/null; echo "Hi"
i{ exec >/dev/null; }; echo "Hi"
polega na tym, że podwójne przekierowanie robidup2(10, 1);
przed zamknięciem fd 10, który jest kopią oryginałustdout
, przed uruchomieniem następnego polecenia (echo
).Dzieje się tak, ponieważ przekierowanie zewnętrzne faktycznie nakłada się na przekierowanie wewnętrzne. Dlatego kopiuje oryginalny
stdout
FD po zakończeniu.źródło