Ostatnim razem, w części 3 tej serii skryptów bash, rozmawialiśmy o parametrach linii poleceń i przełącznikach. Naszym dzisiejszym tematem jest wejście, wyjście i wszystko, co z tym związane.
Znasz już dwie metody pracy z tym, co wypisują skrypty linii komend.skryptów linii poleceń:
- Wyświetlić dane wyjściowe na ekranie.
- Przekierowanie danych wyjściowych do pliku.
Czasami coś musi być wyświetlone na ekranie, a coś musi być zapisane do pliku, więc musisz zrozumieć, jak dane wejściowe i wyjściowe są obsługiwane w Linuksie, co oznacza, że musisz się nauczyć, jak wysyłać wyniki działania skryptów tam, gdzie trzeba. Zacznijmy od omówienia standardowych deskryptorów plików.
Wszystko w Linuksie to pliki, w tym dane wejściowe i wyjściowe. System operacyjny identyfikuje pliki za pomocą deskryptorów.
Każdy proces może mieć do dziewięciu otwartych deskryptorów plików. Powłoka bash rezerwuje pierwsze trzy deskryptory o identyfikatorach 0, 1 i 2. Oto co one oznaczają.
-
0
STDIN
– standardowy strumień wejściowy. -
1
STDOUT
– standardowy strumień wyjściowy. -
2
STDERR
– standardowy strumień błędów.
Te trzy specjalne deskryptory obsługują wejście i wyjście skryptu.
Trzeba dobrze zrozumieć standardowe strumienie. Można je porównać do fundamentu, na którym zbudowana jest interakcja skryptów ze światem zewnętrznym. Rozważmy szczegóły na ich temat.
STDIN
STDIN
– jest to standardowe wejście powłoki. Dla terminala, standardowym wejściem jest klawiatura. Gdy skrypty używają znaku przekierowania wejścia – <
, Linux zastępuje deskryptor pliku standardowego wejścia tym podanym w poleceniu. System odczytuje plik i przetwarza dane tak, jakby były wprowadzane z klawiatury.
Wiele poleceń basha pobiera dane wejściowe z STDIN
chyba że w wierszu poleceń podano plik, z którego mają być pobierane dane. Na przykład, tak jest w przypadku zespołu cat
.
Gdy wprowadzisz polecenie cat
w wierszu poleceń bez podania parametrów, przyjmuje ono dane wejściowe z STDIN
. Po wprowadzeniu kolejnego wiersza, cat
po prostu wypisuje go na ekran.
STDOUT
STDOUT
– standardowe wyjście powłoki. Domyślnie, jest to ekran. Większość poleceń bash wyprowadza dane STDOUT
do konsoli, co powoduje ich wyświetlenie w konsoli. Dane te można przekierować do pliku, dołączając je do jego zawartości za pomocą polecenia >>
.
Mamy więc pewien plik z danymi, do którego możemy dodać inne dane za pomocą tego polecenia:
pwd >> myfile
To, co wypisze pwd
zostanie dodane do pliku myfile
, natomiast dane, które już się w nim znajdują nigdzie nie trafią.
raevskym@DESKTOP-JNF3L6H:~/bash_course$ pwd >> myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
this is old file content
/home/raevskym/Desktop
Do tej pory tak dobrze, ale co jeśli spróbujesz zrobić coś takiego, jak pokazano poniżej, uzyskując dostęp do nieistniejącego plikuxfile
, myśląc o tym wszystkim tak, abymyfile
komunikat o błędzie trafił do pliku.
ls –l xfile > myfile
Po wykonaniu tego polecenia na ekranie zobaczymy komunikaty o błędach.
raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l xfile > myfile
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory
Próba dostępu do nieistniejącego pliku generuje błąd, ale powłoka nie przekierowała komunikatów o błędach do pliku, wyświetlając je. My jednak chcieliśmy, aby komunikaty o błędach trafiały do pliku. Co zrobić? Odpowiedź jest prosta – użyć trzeciego standardowego deskryptora.
STDERR
STDERR
Jest to standardowy strumień błędów powłoki. Domyślnie, uchwyt ten wskazuje na to samo, na co wskazuje STDOUT
, dlatego gdy wystąpi błąd, widzimy komunikat na ekranie.
Załóżmy więc, że chcemy przekierować komunikaty o błędach do, powiedzmy, pliku dziennika lub gdzie indziej, zamiast drukować je na ekranie.
Przekierowanie strumienia błędów
Jak już wiesz, deskryptor pliku dlaSTDERR
to 2. Możemy przekierować błędy, umieszczając ten deskryptor przed poleceniem przekierowania:
ls -l xfile 2>myfile
cat ./myfile
Komunikat o błędzie trafi teraz do pliku myfile
.
raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l xfile 2> myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
ls: cannot access 'xfile': No such flie or directory
Przekierowanie strumieni błędów i wyjścia
Podczas pisania skryptów wiersza poleceń może dojść do sytuacji, w której trzeba zorganizować zarówno przekierowanie komunikatu o błędzie, jak i przekierowanie standardowego wyjścia. Aby to osiągnąć, należy użyć poleceń przekierowania dla odpowiednich deskryptorów, wskazujących pliki, do których powinny trafić błędy i standardowe wyjście:
ls –l myfile xfile anotherfile 2> errorcontent 1> correctcontent
Zobaczmy bliżej:
raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l myfile xfile anotherfile 2> errorcontent 1> correctcontent
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./correctcontent
myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./errorcontent
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory
ls: cannot access 'anotherfile': No such file or directory
Powłoka przekieruje to, co poleceniels
normalnie wysyła doSTDOUT
plikucorrectcontent
ze względu na jego konstrukcję1>
. Komunikaty o błędach, które skończyłyby sięSTDERR
up w pliku, sąerrorcontent
powodowane poleceniem przekierowania2>
.
Jeśli to konieczne, iSTDERR
, iSTDOUT
mogą zostać przekierowane do tego samego pliku za pomocą polecenia&>
:
raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l myfile xfile anotherfile &> content
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./content
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory
ls: cannot access 'anotherfile': No such file or directory
myfile
Po wykonaniu polecenia, to co jest przeznaczoneSTDERR
iSTDOUT
znajduje się w plikucontent
.
Przekierowanie wyjścia w skryptach
Istnieją dwie metody przekierowania wyjścia w skryptach wiersza poleceń:
- Przekierowanie tymczasowe, czyli przekierowanie wyjścia jednej linii.
- Permanentne przekierowanie, czyli przekierowanie całego lub części wyjścia w skrypcie.
Tymczasowe przekierowanie wyjścia
W skrypcie możesz przekierować wyjście jednej linii do STDERR
. Aby to zrobić, wystarczy użyć polecenia przekierowania, podając deskryptor STDERR
, a przed numerem deskryptora umieścić znak ampersandu (&
):
#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"
Jeśli uruchomisz skrypt, obie linie trafią na ekran, ponieważ, jak już wiesz, domyślnie błędy są wyświetlane w tym samym miejscu, co normalne dane.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This is an error
This is normal output
Uruchommy skrypt tak, aby dane wyjścioweSTDERR
trafiały do pliku.
./myscript 2> myfile
Jak widać, teraz zwykłe dane wyjściowe trafiają do konsoli, a komunikaty o błędach do pliku.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 2> content
This is normal output
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./content
This is an error
Stałe przekierowanie wyjścia
Jeśli skrypt musi przekierować wiele wyświetlanych danych, to echo
niewygodne jest dodawanie odpowiedniej komendy do każdego wywołania. Zamiast tego można ustawić, aby dane wyjściowe były przekierowywane do określonego deskryptora na czas działania skryptu, używając polecenia exec
:
#!/bin/bash
exec 1>outfile
echo "This is a test of redirecting all output"
echo "from a shell script to another file."
echo "without having to redirect every line"
Uruchommy skrypt:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat outfile
This is a test of redirecting all output
from a shell script to another file.
without having to redirect every line
Jeśli spojrzymy na plik podany w poleceniu przekierowania wyjścia, okaże się, że wszystko, co wyszło z poleceniaecho
trafiło do tego pliku.
Komendaexec
może być użyta nie tylko na początku skryptu, ale również w innych miejscach:
#!/bin/bash
exec 2>myerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>myfile
echo "This should go to the myfile file"
echo "and this should go to the myerror file" >&2
Tak wygląda sytuacja po uruchomieniu skryptu i obejrzeniu plików, do których przekierowaliśmy dane wyjściowe.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This is the start of the script
now redirecting all output to another location
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myerror
and this should go to the myerror file
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
This should go to the myfile file
Najpierw komendaexec
ustawia przekierowanie wyjścia zSTDERR
do plikumyerror
. Następnie dane wyjściowe kilku poleceń sąecho
syłane doSTDOUT
i wyświetlane na ekranie. Następnie komendaexec
ustawia wysyłanie tego, co trafi doSTDOUT
plikumyfile
, a na koniec, używamy polecenia przekierowaniaSTDERR
w komendzieecho
, co prowadzi do zapisania odpowiedniej linii do pliku.myerror.
Opanowawszy to, możesz przekierować dane wyjściowe tam, gdzie chcesz. Teraz porozmawiajmy o przekierowaniu danych wejściowych.
Przekierowanie danych wejściowych w skryptach
Aby przekierować dane wejściowe, możesz użyć tej samej techniki, której użyliśmy do przekierowania danych wyjściowych. Na przykład, polecenie exec
pozwala na stworzenie źródła danych dla STDIN
pliku:
exec 0< myfile
Ta komenda mówi powłoce, aby jej dane wejściowe pochodziły z pliku myfile
, a nie zwykłego STDIN
. Zobaczmy przekierowanie danych wejściowych w akcji:
#!/bin/bash
exec 0< testfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done
To jest to, co pojawi się na ekranie po uruchomieniu skryptu.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: this is the first line
Line #2: this is the second line
Line #3: this is the third line
W jednym z poprzednich artykułów dowiedziałeś się, jak używać komendyread
do odczytywania danych wejściowych użytkownika z klawiatury. Jeśli przekierujesz dane wejściowe, czyniąc źródłem danych plik, to polecenieread
, próbując odczytać dane zSTDIN
, odczyta je z pliku, a nie z klawiatury.
Niektórzy administratorzy Linuksa używają tego podejścia do odczytywania, a następnie przetwarzania plików logów.
Tworzenie własnych przekierowań wyjścia
Przekierowując wejście i wyjście w skryptach, nie jesteś ograniczony do trzech standardowych deskryptorów plików. Jak wspomniano, możesz mieć do dziewięciu otwartych deskryptorów. Pozostałe sześć, oznaczone numerami od 3 do 8, może być użyte do przekierowania wejścia lub wyjścia. Każdy z nich może być przypisany do pliku i użyty w kodzie skryptu.
Do przypisania deskryptora dla danych wyjściowych można użyć polecenia exec
:
#!/bin/bash
exec 3>myfile
echo "This should display on the screen"
echo "and this should be stored in the file" >&3
echo "And this should be back on the screen"
Po uruchomieniu skryptu część danych wyjściowych trafi na ekran, a część do pliku z deskryptorem 3
.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This should display on the screen
And this should be back on the screen
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
and this should be stored in the file
Tworzenie deskryptorów plików dla danych wejściowych
Możesz przekierować dane wejściowe w skrypcie w taki sam sposób jak dane wyjściowe. Przechowuj STDIN
w innym deskryptorze przed przekierowaniem danych wejściowych.
Po zakończeniu czytania pliku, możesz przywrócić STDIN
i używać go jak zwykle:
#!/bin/bash
exec 6<&0
exec 0< myfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done
exec 0<&6
read -p "Are you done now? " answer
case $answer in
y) echo "Goodbye";;
n) echo "Sorry, this is the end.";;
esac
Testujmy skrypt:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: First line
Line #2: Second line
Line #3: Third line
Are you done now? y
Goodbye
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: First line
Line #2: Second line
Line #3: Third line
Are you done now? n
Sorry, this is the end.
W tym przykładzie deskryptor pliku 6 został użyty do przechowywania referencji doSTDIN
. Następnie wykonano przekierowanie danych wejściowych, źródłem danych dla plikuSTDIN
stało się. Po tym, dane wejściowe dla poleceniaread
pochodziły z przekierowanegoSTDIN
, czyli z pliku.
Po odczytaniu pliku, przywracamySTDIN
jego pierwotny stan, przekierowując go do deskryptora6
. Teraz, w celu sprawdzenia, czy wszystko działa poprawnie, skrypt zadaje użytkownikowi pytanie, czeka na dane wejściowe z klawiatury i przetwarza to, co zostało wprowadzone.
Zamykanie deskryptorów plików
Powłoka automatycznie zamyka deskryptory plików po zakończeniu skryptu. Jednakże, w niektórych przypadkach, konieczne jest ręczne zamknięcie deskryptorów zanim skrypt zakończy pracę. Aby zamknąć uchwyt, należy go przekierować do &-
. Wygląda to tak:
#!/bin/bash
exec 3> myfile
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3
Po wykonaniu skryptu otrzymamy komunikat o błędzie.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
./myscript: line 5: 3: Bad file descriptor
Chodzi o to, że próbowaliśmy uzyskać dostęp do nieistniejącego deskryptora pliku.
Bądź ostrożny przy zamykaniu deskryptorów plików w skryptach. Jeśli wysłałeś dane do pliku, następnie zamknąłeś uchwyt, a następnie otworzyłeś go ponownie, powłoka zastąpi istniejący plik nowym. To znaczy, wszystko, co zostało wcześniej zapisane do tego pliku, zostanie utracone.
Pobieranie informacji o otwartych uchwytach
Aby uzyskać listę wszystkich otwartych deskryptorów w Linuksie, możesz użyć polecenia lsof
. Wiele dystrybucji, jak Fedora, ma narzędzie lsof
w /usr/sbin
. To polecenie jest bardzo przydatne, ponieważ wyświetla informacje o każdym uchwycie otwartym w systemie. Dotyczy to zarówno tego, co jest otwierane przez procesy działające w tle, jak i tego, co jest otwierane przez zalogowanych użytkowników.
Polecenie to posiada wiele klawiszy, przyjrzyjmy się najważniejszym z nich.
-
-p
Pozwala na określenieID
procesu. -
-d
Pozwala na określenie numeru deskryptora, o którym chcemy uzyskać informacje.
W celu poznania PID
bieżącego procesu, można użyć specjalnej zmiennej środowiskowej $$
, do której powłoka zapisuje bieżący PID
.
Klucz ten jest -a
wykorzystywany do wykonania operacji boolean AND
na wynikach zwróconych przez użycie pozostałych dwóch kluczy:
raevskym@DESKTOP-JNF3L6H:~/bash_course$ lsof -a -p $$ -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
myscript 24 raevskym 0u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 1u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 2u CHR 4,1 25332 /dev/tty1
Typ plików związanych zSTDIN
STDOUT
iSTDERR —
CHR (tryb znakowy). Ponieważ wszystkie one wskazują na terminal, nazwa pliku odpowiada nazwie urządzenia przypisanego do terminala. Wszystkie trzy standardowe pliki są dostępne do odczytu i zapisu.
Przyjrzyjrzyjmy się wywołaniu polecenialsof
ze skryptu, w którym oprócz standardowego, otwarte są inne deskryptory:
#!/bin/bash
exec 3> myfile1
exec 6> myfile2
exec 7< myfile3
lsof -a -p $$ -d 0,1,2,3,6,7
Tak się stanie, jeśli uruchomimy ten skrypt.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
myscript 24 raevskym 0u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 1u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 2u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 3w REG 0,14 0 13510 ~/bash_course/myfile1
myscript 24 raevskym 6w REG 0,14 0 13229 ~/bash_course/myfile2
Skryptor otworzył dwa deskryptory dla wyjścia (3
i6
) i jeden dla wejścia (7
). Podane są tu również ścieżki do plików używanych do konfiguracji deskryptorów.
Tłumienie danych wyjściowych
Czasami trzeba się upewnić, że polecenia w skrypcie, które na przykład mogą być wykonywane jako proces w tle, nie wyświetlają niczego na ekranie. Aby to zrobić, można przekierować wyjście do /dev/null
. Jest to coś w rodzaju „czarnej dziury”.
Na przykład, oto jak stłumić komunikaty o błędach:
ls -al badfile anotherfile 2> /dev/null
Tego samego podejścia używa się, gdy na przykład trzeba wyczyścić plik bez usuwania go:
cat /dev/null > myfile
Wyniki
Dzisiaj dowiedziałeś się, jak działa wejście i wyjście w skryptach wiersza poleceń. Teraz wiesz jak obsługiwać deskryptory plików, tworzyć, przeglądać i zamykać je, wiesz o przekierowywaniu strumieni wejściowych, wyjściowych i błędów. To wszystko jest bardzo ważne w tworzeniu skryptów basha.
Następnym razem porozmawiamy o sygnałach linuksowych, jak je obsługiwać w skryptach, jak uruchamiać zaplanowane zadania i zadania w tle.