Jak działają deskryptory plików?

Czy ktoś może mi powiedzieć, dlaczego to nie działa? Bawię się z deskryptorami plików, ale czuję się trochę zagubiony.

#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

Pierwsze trzy linie działają dobrze, ale ostatnie dwa błędy out. Dlaczego?

Author: wjandrea, 2011-08-16

3 answers

Deskryptory plików 0, 1 i 2 są odpowiednio dla stdin, stdout i stderr.

Deskryptory plików 3, 4, .. 9 to dodatkowe pliki. Aby z nich korzystać, musisz je najpierw otworzyć. Na przykład:

exec 3<> /tmp/foo  #open fd 3.
echo "test" >&3
exec 3>&- #close fd 3.

Aby uzyskać więcej informacji, zajrzyj do Advanced Bash-Scripting Guide: Rozdział 20. Przekierowanie We / Wy .

 80
Author: dogbane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 17:13:54

To stare pytanie, ale jedno wymaga wyjaśnienia.

Podczas gdy odpowiedzi Carla Noruma i dogbane ' a są poprawne, założeniem jest zmienić skrypt, aby działał .

Chciałbym zwrócić uwagę na to, że nie musisz zmieniać skryptu :

#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

To działa, jeśli wywołasz to inaczej:

./fdtest 3>&1 4>&1

Co oznacza przekierowanie deskryptorów plików 3 i 4 na 1 (co jest standardowym wyjściem).

Chodzi o to, że że skrypt jest idealny , jeśli chce pisać do deskryptorów innych niż 1 i 2 (stdout i stderr) , jeśli te deskryptory są dostarczane przez proces macierzysty .

Twój przykład jest całkiem interesujący, ponieważ ten skrypt może zapisywać do 4 różnych plików:

./fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt

Teraz masz wyjście w 4 oddzielnych plikach:

$ for f in file*; do echo $f:; cat $f; done
file1.txt:
This
file2.txt:
is
file3.txt:
a
file4.txt:
test.

Co jest bardziej interesujące jest to, że twój program nie musi mieć uprawnień do zapisu te pliki, bo ich nie otwiera.

Na przykład, gdy uruchamiam sudo -s, aby zmienić użytkownika na root, utwórz katalog jako root i spróbuj uruchomić następujące polecenie jako mój zwykły użytkownik (w moim przypadku rsp) w następujący sposób:

# su rsp -c '../fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt'

Dostaję błąd:

bash: file1.txt: Permission denied

Ale jeśli zrobię przekierowanie poza su:

# su rsp -c '../fdtest' >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt

(zwróć uwagę na różnicę w pojedynczych cudzysłowach) to działa i dostaję:

# ls -alp
total 56
drwxr-xr-x 2 root root 4096 Jun 23 15:05 ./
drwxrwxr-x 3 rsp  rsp  4096 Jun 23 15:01 ../
-rw-r--r-- 1 root root    5 Jun 23 15:05 file1.txt
-rw-r--r-- 1 root root   39 Jun 23 15:05 file2.txt
-rw-r--r-- 1 root root    2 Jun 23 15:05 file3.txt
-rw-r--r-- 1 root root    6 Jun 23 15:05 file4.txt

Które są 4 plikami należącymi do roota w katalog należący do roota - , mimo że skrypt nie miał uprawnień do tworzenia tych plików.

Innym przykładem może być użycie chroot jail lub kontenera i uruchomienie programu wewnątrz, w którym nie miałby on dostępu do tych plików, nawet jeśli byłby uruchomiony jako root i nadal przekierowywałby te deskryptory zewnętrznie tam, gdzie potrzebujesz, nie dając dostępu do całego systemu plików lub czegokolwiek innego temu skryptowi.

Chodzi o to, że odkryłeś bardzo ciekawy i przydatny mechanizm . Nie musisz otwierać wszystkich plików wewnątrz skryptu, jak sugerowano w innych odpowiedziach. Czasami przydatne jest przekierowanie ich podczas wywoływania skryptu.

Podsumowując to , to:

echo "This"

Jest właściwie równoważne:

echo "This" >&1

I uruchamianie programu jako:

./program >file.txt

Jest tym samym co:

./program 1>file.txt

Liczba 1 jest tylko liczbą domyślną i jest to stdout.

Ale nawet to program:

#!/bin/bash
echo "This"

Może spowodować błąd "złego deskryptora". Jak? Po uruchomieniu jako:

./fdtest2 >&-

Wyjście będzie:

./fdtest2: line 2: echo: write error: Bad file descriptor

Dodanie >&- (co jest takie samo jak 1>&-) oznacza zamknięcie standardowego wyjścia. Dodanie 2>&- oznaczałoby zamknięcie stderr.

Możesz nawet zrobić bardziej skomplikowaną rzecz . Twój oryginalny scenariusz:

#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

Po uruchomieniu Z just:

./fdtest

Druki:

This
is
./fdtest: line 4: 3: Bad file descriptor
./fdtest: line 5: 4: Bad file descriptor

Ale można zrobić deskryptory 3 i 4 działają, ale numer 1 fail by running:

./fdtest 3>&1 4>&1 1>&-

Wychodzi:

./fdtest: line 2: echo: write error: Bad file descriptor
is
a
test.

Jeśli chcesz, aby deskryptory 1 i 2 zawiedły, uruchom go w następujący sposób:

./fdtest 3>&1 4>&1 1>&- 2>&-

Otrzymujesz:

a
test.
Dlaczego? Nic nie zawiodło? udało się ale bez stderr (deskryptor pliku numer 2) nie widziałeś komunikatów o błędach!

Myślę, że bardzo przydatne jest eksperymentowanie w ten sposób, aby poczuć, jak deskryptory i ich przekierowania działają.

Twój scenariusz jest bardzo rzeczywiście ciekawy przykład - i twierdzę, że wcale nie jest zepsuty, po prostu źle go używałeś! :)

 47
Author: rsp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-15 09:55:35

To nie działa, ponieważ te deskryptory plików nie wskazują na nic! Standardowe domyślne deskryptory plików to standardowe wejście 0, standardowe Wyjście 1 i standardowy strumień błędów 2. Ponieważ skrypt nie otwiera żadnych innych plików, nie ma innych ważnych deskryptorów plików. Możesz otworzyć plik w bash używając exec. Oto modyfikacja twojego przykładu:

#!/bin/bash
exec 3> out1     # open file 'out1' for writing, assign to fd 3
exec 4> out2     # open file 'out2' for writing, assign to fd 4

echo "This"      # output to fd 1 (stdout)
echo "is" >&2    # output to fd 2 (stderr)
echo "a" >&3     # output to fd 3
echo "test." >&4 # output to fd 4

A teraz go uruchomimy:

$ ls
script
$ ./script 
This
is
$ ls
out1    out2    script
$ cat out*
a
test.
$

Jak widać, dodatkowe wyjście zostało wysłane do żądanego pliki.

 18
Author: Carl Norum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 17:16:56