programing tip

파일 설명자는 어떻게 작동합니까?

itbloger 2020. 11. 10. 07:54
반응형

파일 설명자는 어떻게 작동합니까?


누군가 이것이 작동하지 않는 이유를 말해 줄 수 있습니까? 파일 디스크립터를 가지고 놀지 만 조금 잃어버린 느낌이 듭니다.

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

처음 세 줄은 잘 실행되지만 마지막 두 줄은 오류가 발생합니다. 왜?


파일 설명자 0, 1 및 2는 각각 stdin, stdout 및 stderr 용입니다.

파일 설명자 3, 4, .. 9는 추가 파일 용입니다. 사용하려면 먼저 열어야합니다. 예를 들면 :

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

자세한 내용은 Advanced Bash-Scripting Guide : Chapter 20. I / O Redirection을 참조하십시오 .


오래된 질문이지만 한 가지 설명이 필요합니다 .

Carl Norum과 dogbane의 답변은 정확하지만 스크립트를 작동 하도록 변경 하는 것으로 가정 합니다 .

제가 지적하고 싶은 것은 스크립트를 변경할 필요가 없다는 것입니다 .

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

다르게 호출하면 작동합니다.

./fdtest 3>&1 4>&1

이는 파일 설명자 3과 4를 1 (표준 출력)로 리디렉션하는 것을 의미합니다.

요점은 스크립트가 부모 프로세스에 의해 제공된 설명 자인 경우 1과 2 (stdout 및 stderr) 이외의 설명자에 쓰기를 원하는 경우 완벽하게 괜찮다 는 것 입니다.

이 스크립트는 4 개의 다른 파일에 쓸 수 있기 때문에 귀하의 예제는 실제로 매우 흥미 롭습니다.

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

이제 4 개의 개별 파일에 출력이 있습니다.

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

무엇 보다 흥미로운 그것에 대해 것은 실제로을 열 수 없기 때문에 프로그램이 해당 파일에 대한 쓰기 권한이 필요하지 않습니다.

예를 들어, sudo -s사용자를 루트로 변경하기 위해 실행 하면 루트로 디렉토리를 만들고 다음과 같이 일반 사용자 (제 경우에는 rsp)로 다음 명령을 실행 해보십시오.

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

오류가 발생합니다.

bash: file1.txt: Permission denied

하지만 외부 리디렉션을 수행하면 su:

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

(작은 따옴표의 차이점에 유의하십시오) 작동 하고 다음을 얻습니다.

# 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

이 파일은 루트가 소유 한 디렉토리에서 루트가 소유 한 4 개의 파일입니다 . 스크립트에 해당 파일을 생성 할 권한이 없더라도 말입니다 .

또 다른 예는 chroot jail 또는 컨테이너를 사용하고 루트로 실행 되었더라도 해당 파일에 액세스 할 수없는 내부 프로그램을 실행하고 실제로 전체 파일에 대한 액세스 권한을 부여하지 않고 필요한 곳에서 해당 설명자를 외부로 리디렉션하는 것입니다. 이 스크립트에 대한 시스템 또는 기타.

요점은 매우 흥미롭고 유용한 메커니즘을 발견 했다는 입니다. 다른 답변에서 제안한 것처럼 스크립트 내부의 모든 파일을 열 필요는 없습니다. 때로는 스크립트 호출 중에 리디렉션하는 것이 유용합니다.

요약하면 다음 과 같습니다.

echo "This"

실제로 다음과 같습니다.

echo "This" >&1

프로그램을 다음과 같이 실행하십시오.

./program >file.txt

와 같다:

./program 1>file.txt

숫자 1은 기본 숫자이며 stdout입니다.

하지만이 프로그램도 :

#!/bin/bash
echo "This"

"잘못된 설명자"오류가 발생할 수 있습니다. 어떻게? 다음과 같이 실행하면

./fdtest2 >&-

출력은 다음과 같습니다.

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

추가 >&-(과 동일 1>&-)는 표준 출력을 닫는 것을 의미합니다. 추가 2>&-는 stderr을 닫는 것을 의미합니다.

더 복잡한 일도 할 수 있습니다 . 원본 스크립트 :

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

다음과 같이 실행할 때 :

./fdtest

인쇄물:

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

But you can make descriptors 3 and 4 work, but number 1 fail by running:

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

It outputs:

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

If you want descriptors both 1 and 2 fail, run it like this:

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

You get:

a
test.

Why? Didn't anything fail? It did but with no stderr (file descriptor number 2) you didn't see the error messages!

I think it's very useful to experiment this way to get a feeling of how the descriptors and their redirection work.

Your script is a very interesting example indeed - and I argue that it is not broken at all, you were just using it wrong! :)


It's failing because those file descriptors don't point to anything! The normal default file descriptors are the standard input 0, the standard output 1, and the standard error stream 2. Since your script isn't opening any other files, there are no other valid file descriptors. You can open a file in bash using exec. Here's a modification of your example:

#!/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

And now we'll run it:

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

As you can see, the extra output was sent to the requested files.


To add on to the answer from rsp and respond the question in the comments of that answer from @MattClimbs.

You can test if the file descriptor is open or not by attempting to redirect to it early and if it fails, open the desired numbered file descriptor to something like /dev/null. I do this regularly within scripts and leverage the additional file descriptors to pass back additional details or responses beyond return #.

script.sh

#!/bin/bash
2>/dev/null >&3 || exec 3>/dev/null
2>/dev/null >&4 || exec 4>/dev/null

echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4

The stderr is redirected to /dev/null to discard the possible bash: #: Bad file descriptor response and the || is used to process the following command exec #>/dev/null when the previous one exits with a non zero status. In the event that the file descriptor is already opened, the two tests would return a zero status and the exec ... command would not be executed.

Calling the script without any redirections yields:

# ./script.sh
This
is

In this case, the redirections for a and test are shipped off to /dev/null

Calling the script with a redirection defined yields:

# ./script.sh 3>temp.txt 4>>temp.txt
This
is
# cat temp.txt
a
test.

The first redirection 3>temp.txt overwrites the file temp.txt while 4>>temp.txt appends to the file.

In the end, you can define default files to redirect to within the script if you want something other than /dev/null or you can change the execution method of the script and redirect those extra file descriptors anywhere you want.

참고URL : https://stackoverflow.com/questions/7082001/how-do-file-descriptors-work

반응형