Jak przekierować wyjście całego skryptu powłoki w samym skrypcie?


205

Czy można przekierować gdzieś wszystkie wyniki skryptu powłoki Bourne'a, ale za pomocą poleceń powłoki w samym skrypcie?

Przekierowanie wyjścia pojedynczego polecenia jest łatwe, ale chcę czegoś takiego:

#!/bin/sh
if [ ! -t 0 ]; then
    # redirect all of my output to a file here
fi

# rest of script...

Znaczenie: jeśli skrypt jest uruchamiany nieinteraktywnie (na przykład cron), zapisz dane wyjściowe wszystkiego do pliku. Jeśli uruchamiany jest interaktywnie z powłoki, pozwól, aby wyjście przechodziło na standardowe wyjście jak zwykle.

Chcę to zrobić dla skryptu normalnie uruchamianego przez okresowe narzędzie FreeBSD. Jest to część codziennego biegu, którego zwykle nie obchodzi mnie codziennie w wiadomości e-mail, więc nie wysyłam go. Jeśli jednak coś w tym konkretnym skrypcie zawiedzie, jest to dla mnie ważne i chciałbym móc przechwytywać i wysyłać pocztą e-mail wyniki tej części codziennych zadań.

Aktualizacja: odpowiedź Jozuego jest natychmiastowa, ale chciałem również zapisać i przywrócić stdout i stderr dookoła całego skryptu, co odbywa się w następujący sposób:

# save stdout and stderr to file descriptors 3 and 4, then redirect them to "foo"
exec 3>&1 4>&2 >foo 2>&1

# ...

# restore stdout and stderr
exec 1>&3 2>&4

2
Testowanie na $ TERM nie jest najlepszym sposobem na testowanie w trybie interaktywnym. Zamiast tego sprawdź, czy stdin jest tty (test -t 0).
— Chris Jester-Young,

2
Innymi słowy: jeśli [! -t 0]; następnie exec> somefile 2> & 1; fi
— Chris Jester-Young

1
Zobacz tutaj całą dobroć: http://tldp.org/LDP/abs/html/io-redirection.html Zasadniczo to, co powiedział Joshua. plik exec> przekierowuje stdout do określonego pliku, plik exec <plik zastępuje stdin przez plik itp. Jest taki sam jak zwykle, ale używa exec (więcej szczegółów znajduje się w man exec).
— Loki

W sekcji dotyczącej aktualizacji powinieneś także zamknąć FD 3 i 4, tak jak: exec 1>&3 2>&4 3>&- 4>&-
— Gurjeet Singh

Odpowiedzi:


173

Odpowiedź na pytanie w formie zaktualizowanej.

#...part of script without redirection...

{
    #...part of script with redirection...
} > file1 2>file2 # ...and others as appropriate...

#...residue of script without redirection...

Nawiasy klamrowe „{...}” zapewniają jednostkę przekierowania we / wy. Nawiasy klamrowe muszą pojawiać się tam, gdzie może pojawić się polecenie - w uproszczeniu, na początku linii lub po średniku. ( Tak, można to sprecyzować; jeśli chcesz się spierać, daj mi znać. )

Masz rację, że możesz zachować oryginalny stdout i stderr z pokazanymi przekierowaniami, ale zwykle ludzie, którzy muszą później utrzymać skrypt, łatwiej zrozumieć, co się dzieje, jeśli zakodujesz przekierowany kod, jak pokazano powyżej.

Odpowiednie sekcje podręcznika Bash to polecenia grupujące i przekierowanie we / wy . Odpowiednie odcinki opisie powłoki POSIX są Compound poleceń i I / O przekierowania . Bash ma kilka dodatkowych notacji, ale poza tym jest podobny do specyfikacji powłoki POSIX.


3
Jest to o wiele wyraźniejsze niż zapisywanie oryginalnych deskryptorów i przywracanie ich później.
— Steve Madsen

23
Musiałem zrobić Google, aby zrozumieć, co to naprawdę robi, więc chciałem się tym podzielić. Nawiasy klamrowe stają się „blokiem kodu” , który w efekcie tworzy anonimową funkcję . Wszystkie dane wyjściowe w bloku kodu można następnie przekierować (patrz przykład 3-2 z tego łącza). Należy również pamiętać, że nawiasy klamrowe nie uruchamiają podpowłoki , ale podobne przekierowania we / wy można wykonać za pomocą podpowłok za pomocą nawiasów.
— Chris

3
Podoba mi się to rozwiązanie bardziej niż inne. Nawet osoba posiadająca tylko najbardziej podstawową wiedzę na temat przekierowania wejścia / wyjścia może zrozumieć, co się dzieje. Plus, to bardziej szczegółowe. Jako Pythoner uwielbiam gadatliwie.
— John Red

Lepiej >>. Niektórzy ludzie mają nawyk >. Dołączanie jest zawsze bezpieczniejsze i bardziej zalecane niż przytłaczanie. Ktoś napisał aplikację, która używa standardowego polecenia kopiowania, aby wyeksportować niektóre dane do tego samego miejsca docelowego.
— neverMind9

Ta aplikacja to ccc.bmw71 (.pro) (3C Battery Monitor Widget, dawniej „Battery Monitor Widget”) zawsze eksportuje historię baterii do /sdcard/bmw_history.txt. Jeśli już istnieje, zgadnij co, OVERWRITE! To spowodowało, że przypadkowo straciłem trochę historii baterii. Ustawiłem limit w dniach od 30 na bardzo wysoką liczbę, co unieważniło go i przywróciłem do 30. Chciałem wyeksportować bieżącą historię baterii przed zaimportowaniem istniejącej kopii zapasowej. To się stało.
— neverMind9

175

Zazwyczaj umieszczamy jeden z nich na górze skryptu lub w jego pobliżu. Skrypty, które analizują wiersze poleceń, dokonają przekierowania po analizie.

Wyślij stdout do pliku

exec > file

z stderr

exec > file                                                                      
exec 2>&1

dołącz do pliku zarówno stdout, jak i stderr

exec >> file
exec 2>&1

Jak wspomniał Jonathan Leffler w swoim komentarzu :

execma dwie osobne prace. Pierwszym z nich jest zastąpienie aktualnie wykonywanej powłoki (skryptu) nowym programem. Drugi polega na zmianie przekierowań we / wy w bieżącej powłoce. Wyróżnia się to brakiem argumentu exec.


7
Mówię też, że dodaj 2> i 1 na końcu, żeby stderr też został złapany. :-)
— Chris Jester-Young,

7
Gdzie je umieszczasz? Na początku skryptu?
— colan

4
W tym rozwiązaniu należy również zresetować przekierowanie, z którego wychodzi skrypt. Następna odpowiedź Jonathana Lefflera jest w tym sensie bardziej „odporna na błędy”.
— Chuim

6
@JohnRed: execma dwa osobne zadania. Jednym z nich jest zastąpienie bieżącego skryptu innym poleceniem, przy użyciu tego samego procesu - określasz drugie polecenie jako argument exec(i możesz dostosowywać przekierowania we / wy podczas tej operacji ). Drugim zadaniem jest zmiana przekierowań we / wy w bieżącym skrypcie powłoki bez zastępowania go. Notacja ta wyróżnia się tym, że nie ma polecenia jako argumentu exec. Zapis w tej odpowiedzi dotyczy wariantu „Tylko we / wy” - zmienia tylko przekierowanie i nie zastępuje uruchomionego skryptu. ( setPolecenie jest podobnie wielofunkcyjne).
— Jonathan Leffler

18
exec > >(tee -a "logs/logdata.log") 2>&1drukuje dzienniki na ekranie, a także zapisuje je w pliku
— shriyog

32

Możesz ustawić cały skrypt w taki sposób:

main_function() {
  do_things_here
}

następnie na końcu skryptu:

if [ -z $TERM ]; then
  # if not run via terminal, log everything into a log file
  main_function 2>&1 >> /var/log/my_uber_script.log
else
  # run via terminal, only output to screen
  main_function
fi

Alternatywnie możesz zalogować wszystko do pliku dziennika przy każdym uruchomieniu i nadal wyprowadzać go na standardowe wyjście, po prostu wykonując:

# log everything, but also output to stdout
main_function 2>&1 | tee -a /var/log/my_uber_script.log

Czy chodziło Ci o main_function >> /var/log/my_uber_script.log 2> & 1
— Felipe Alvarez

Lubię używać main_function w takiej rurze. Ale w tym przypadku skrypt nie zwraca oryginalnej wartości zwracanej. W przypadku bash powinieneś wyjść, a następnie użyj „exit $ {PIPESTATUS [0]}”.
— rudimeier

7

Aby zapisać oryginalny stdout i stderr, możesz użyć:

exec [fd number]<&1 
exec [fd number]<&2

Na przykład poniższy kod wypisze „walla1” i „walla2” do pliku dziennika ( a.txt), „walla3” na standardowe wyjście, „walla4” na standardowe wyjście.

#!/bin/bash

exec 5<&1
exec 6<&2

exec 1> ~/a.txt 2>&1

echo "walla1"
echo "walla2" >&2
echo "walla3" >&5
echo "walla4" >&6

1
Zwykle lepiej byłoby użyć exec 5>&1i exec 6>&2, używając notacji przekierowania wyjścia, niż notacji przekierowania wejścia dla wyjść. Unikasz tego, ponieważ kiedy skrypt jest uruchamiany z terminala, standardowe dane wejściowe są również zapisywalne, a zarówno standardowe dane wyjściowe, jak i standardowy błąd są czytelne na podstawie (czy też „wice”?) Historycznego dziwactwa: terminal jest otwarty dla odczyt i zapis oraz ten sam opis otwartego pliku jest używany dla wszystkich trzech standardowych deskryptorów plików we / wy.
— Jonathan Leffler

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.