Mam pewien skrypt bash, który chce zachować oryginalną /dev/stdout
lokalizację przed zamianą 1. deskryptora pliku na inną lokalizację.
Oczywiście napisałem coś takiego
old_stdout=$(readlink -f /dev/stdout)
I to nie zadziałało. Bardzo szybko rozumiem na czym polegał problem:
test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
Oczywiście $()
działa w podpowłoce, która jest potokowana do powłoki nadrzędnej.
Pytanie brzmi: czy istnieje niezawodny (w zakresie przenośności między dystrybucjami Linuksa) sposób na zapisanie /dev/stdout
lokalizacji jako łańcucha w skrypcie bash?
bash
io-redirection
io
alexey.e.egorov
źródło
źródło
/dev/stdout
rozwiąże problem z drukowaniem wiadomości w trybie cichym. Alternatywą jest przekierowywanie każdej innej akcji, która generuje dane wyjściowe, i jest ich całkiem sporo. Około 100 razy więcej niż komunikaty interakcji użytkownika.stderr
. Właśnie dlategostderr
domyślnie wyświetlane są monity .stderr
należy również przekierować i zapisać, ponieważ skrypt wywołuje wiele programów zewnętrznych, a wszystkie możliwe komunikaty o błędach należy zebrać i zarejestrować.Odpowiedzi:
Aby zapisać deskryptor pliku, powiel go na innym dysku. Zapisanie ścieżki do odpowiedniego pliku nie wystarczy, trzeba zapisać tryb otwierania, flagi otwarcia, bieżącą pozycję w pliku i tak dalej. I oczywiście dla anonimowych rur lub gniazd nie działałoby, ponieważ nie mają one ścieżki. To, co chcesz zapisać, to otwarty opis pliku , do którego odnosi się fd, a powielenie fd faktycznie zwraca nowy fd do tego samego opisu otwartego pliku .
Aby powielić deskryptor pliku na innym, w powłoce Bourne'a, składnia jest następująca:
Powyżej, fd 1 jest duplikowane na fd 3.
Cokolwiek fd 3 było już otwarte wcześniej, zostanie zamknięte, ale należy pamiętać, że fds 3 do 9 (zwykle więcej, do 99 z
yash
) są zarezerwowane do tego celu (i nie mają specjalnego znaczenia sprzecznego z 0, 1 lub 2), Shell wie, że nie będzie ich używać do własnych celów wewnętrznych. Jedynym powodem, dla którego fd 3 byłby wcześniej otwarty, było to, że zrobiłeś to w skrypcie 1 lub wyciekł z niego dzwoniący.Następnie możesz zmienić stdout na coś innego:
A później, aby przywrócić standardowe wyjście:
(
3>&-
zamykanie deskryptora pliku, którego już nie potrzebujemy).Problem polega na tym, że z wyjątkiem ksh każde kolejne polecenie
exec 3>&1
odziedziczy fd 3. To przeciek fd. Zasadniczo nie jest to wielka sprawa, ale może to powodować problemy.ksh
ustawia flagę close-on-exec na tych fds (dla fds powyżej 2), ale nie inne powłoki i inne powłoki nie mają możliwości ręcznego ustawienia tej flagi.Obejściem drugiej powłoki jest zamknięcie fd 3 dla każdego polecenia, na przykład:
Nieporęczny. Tutaj najlepszym sposobem byłoby w ogóle nie używać
exec
, ale przekierowywać grupy poleceń:Tam jest to powłoka, która dba o zapisanie stdout i przywrócenie go później (i robi to wewnętrznie, powielając go na fd (powyżej 9, powyżej 99 dla
yash
) z ustawioną flagą close-on-exec ).Uwaga 1
Teraz zarządzanie tymi fds 3 do 9 może być kłopotliwe i problematyczne, jeśli używasz ich intensywnie lub w funkcjach, szczególnie jeśli twój skrypt używa kodu innej firmy, który z kolei może z nich korzystać.
Niektóre muszle (
zsh
,bash
,ksh93
, wszystkie dodane funkcji ( sugerowane przez Oliver Kiddle zzsh
) w tym samym czasie w 2005 roku po to zostało omówione między ich twórców) mają alternatywną składnię przypisywania pierwszego wolnego fd powyżej 10 zamiast co pomaga w tym przypadku:źródło
rc.local
usługi, np. Więc naprawdę powinieneś użyć czegoś takiegoexec {FD}>&1
lub czegoś takiego. Ale jest to obsługiwane tylko w bash 4, co jest naprawdę smutne. To nie jest tak naprawdę przenośne.eval "exec $i>&1"
jest rzeczą, której chciałbym uniknąć, ze względu na nieporęczność. Czy naprawdę mogę polegać na tym, że fds powyżej 9 byłby wtedy darmowy?bash
pozwoli ci strzelić sobie w stopę.Jak widać, skrypty bash nie są jak zwykły język programowania, w którym można przypisywać deskryptory plików.
Najprostszym rozwiązaniem jest użycie podpowłoki do uruchomienia tego, co chcesz przekierować, aby przetwarzanie mogło zostać przywrócone do najwyższej powłoki, która ma nienaruszone standardowe I / O.
Alternatywnym rozwiązaniem byłoby
tty
zidentyfikowanie urządzenia TTY i kontrolowanie operacji we / wy w skrypcie. Na przykład:i wtedy możesz ...
źródło
$$
dostarczy ci bieżący PID procesu, w przypadku powłoki interaktywnej lub skryptu odpowiedni PID powłoki.Możesz więc użyć:
Przykład:
źródło
/proc
strukturze powoduje problemy z przenośnością, podobnie jak używanie,/dev/stdout
jak wspomniano w pytaniu./proc
strukturze? Działa na każdym Linuksie, który maprocfs
...procfs
prawie zawsze jest obecny, ale często widzimy pytania dotyczące przenośności, a dobra metodologia programowania obejmuje rozważenie możliwości przenoszenia na inne systemy.bash
może działać na wielu systemach operacyjnych.