파일 디스크립터가 유효한지 테스트


12

bash 스크립트가 파일 디스크립터 (FD)에 추가 정보가 열려있을 때 3 이상으로 출력되도록하고 싶습니다. FD가 열려 있는지 테스트하기 위해 다음과 같은 트릭을 고안했습니다.

if (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

이것은 내 필요에 충분하지만 FD가 유효한 경우 더 관용적 인 테스트 방법이 있는지 궁금합니다. 나는 특히의 매핑이 존재하는지 여부에 대해 관심이 있어요 fcntl(1)(FD 플래그의 검색을 허용 할 쉘 명령에 콜을 O_WRONLY하고 O_RDWRFD가 쓸 수 있는지 여부를 테스트하고, O_RDONLY그리고 O_RDWR상기 FD를 읽을 수 있는지 여부를 테스트).

답변:


12

에서 ksh(모두 AT & T와 pdksh 같은 변형) 또는 zsh, 당신은 할 수 있습니다 :

if print -nu3; then
  echo fd 3 is writeable
fi

그들은 그 fd에 아무것도 쓰지 않지만 여전히 fd가 쓰기 가능한지 확인하고 (를 사용하여 fcntl(3, F_GETFL)) 그렇지 않으면 오류를보고합니다.

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing

(로 리디렉션 할 수 있음 /dev/null).

를 사용하면 fd가 쓰기 가능하다는 것을 보장하지는 않지만 (또는 외부 유틸리티 ( / ...)를 호출하여) 접근 방식과 같은 성공 bash여부를 확인하는 것이 유일한 옵션이라고 생각합니다 .dup()zshperlfcntl()

에서 bash(대부분의 쉘과 마찬가지로) (...)대신 을 사용 {...;}하면 추가 프로세스가 발생합니다. 당신이 사용할 수있는:

if { true >&3; } 2<> /dev/null

대신 포크를 피하십시오 (복합 명령을 재지 정하는 것이 항상 서브 쉘을 발생시키는 Bourne 쉘 제외). 사용하지 않는 :대신에 true그 A의로 특별한 배쉬는 POSIX 호환 모드에있을 때 내장, 그래서 출구 쉘을 일으킬 것입니다.

그러나 다음과 같이 단축 할 수 있습니다.

if { >&3; } 2<> /dev/null

@ mikeserve, 다시 : 당신의 편집은 무엇 <>입니까? 쉘이 stderr에서 읽히지 않을 것입니다. 왜 읽기 + 쓰기로 열려고 하시겠습니까? 본질적인 일무엇 을 의미 합니까? ?
Stéphane Chazelas

7

POSIX 응용 프로그램 사용법 설명에는 다음이 있습니다.command

경우에 따라 특수 내장 기능의 특수 특성을 억제하면 몇 가지 장점이 있습니다. 예를 들면 다음과 같습니다.

command exec > unwritable-file

비 대화식 스크립트가 중단되지 않으므로 스크립트가 출력 상태를 확인할 수 있습니다.

이것이 바로 당신이 할 수있는 이유입니다.

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2<>/dev/null

또는...

{ command >&3
  printf %s\\n%.0d  string "0$(($??8:0))" >&"$(($??1:3))"
} 2<>/dev/null

이것은 문자열 뒤에 \nstdout 또는 3에 ewline을 쓰고 3이 열리지 않을 때 여전히 0이 아닌 종료 상태를 전달합니다. 왜냐하면 $?8에서 08% 10 진수로 변환하지 못하고 전혀 잘리지 않기 때문에 3에서 열리지 않으면 종료되지 않습니다. 8 진수 00 .

또는...

command exec >&3 || handle_it

그러나를 사용하는 경우 다음을 ksh93수행 할 수 있습니다.

fds

열린 파일 디스크립터 목록. -l그들이 어디로 가는지보기 위해 추가 하십시오.


3

열린 파일 디스크립터는에서 찾을 수 있습니다 /proc/<pid>/fd. 예를 들어, 현재 쉘의 열린 파일 디스크립터를 나열하려면 ls -l /proc/$$/fd다음과 같이 제공해야합니다.

total 0
lrwx------ 1 testuser testuser 64 jun  1 09:11 0 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 1 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 2 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:39 255 -> /dev/pts/3

다음을 사용하여 파일을 열 때 :

touch /tmp/myfile
exec 7</tmp/myfile

새 목록으로 작성되어야합니다 ls -l /proc/$$/fd.

lr-x------ 1 testuser testuser 64 jun  1 09:11 7 -> /tmp/myfile

파일 디스크립터를 다시 사용하여 닫으면 더 이상 exec 7>&-나열되지 /proc/$$/fd않습니다.


2
이 모든 것은 Linux에만 해당됩니다. FWIW.
lcd047

1
Linux 및 Solaris (10 및 11)에서 테스트했습니다. 차이점은 Linux에서 연결 pfiles <pid>ls -l표시하는 동안 어떤 파일 디스크립터가 어떤 파일에 연결되어 있는지 확인 해야한다는 것 입니다.
Lambert

나는 압축의 콤팩트를 [ -e /proc/$$/fd/3 ]좋아하지만 FreeBSD 및 더 이상 다른 **에서도 사용되지 않기 때문에 procfs에 의존하지 않는 것을 선호합니다.
Witiko

1
pfiles <pid>또는 사용 lsof -p <pid>중인 파일 설명자를 사용하는 대신 사용할 수있는 대안을 제공합니다.
Lambert

1
/procOpenBSD에는 전혀 존재하지 않습니다. FreeBSD 및 NetBSD에서는 mount명시 적 으로 -ed 이어야 /proc/<PID>하고 하위 디렉토리가 없어야 fd합니다.
lcd047

3

당신의 트릭은 귀엽게 보입니다. 그러나 관용적 인 방법으로 왜 당신이 사용하지 않은지 궁금합니다.

if ( exec 1>&3 ) 2>&-

이것은 실제로 더 깨끗한 방법입니다.
Witiko

5
서브 쉘을 생성하지만 대부분의 쉘은 프로세스를 중단시키는 것을 의미합니다. fd가 쓰기 가능하다는 것을 보장하지는 않습니다. { true >&3; } 2> /dev/null포크를 피하기 위해 사용할 수 있습니다 . 또는 { command exec >&3; } 2> /dev/nullstdout을 리디렉션하려는 경우.
Stéphane Chazelas 2016 년

@ 스테판; @Witiko가 발명 한 서브 쉘 트릭은 경로 재 지정을 사용하여 경로 재 지정을 사용할 때 현재 환경의 파일 디스크립터에 영향을 미치지 않는 것입니다. - 언급 한 "쓰기 가능 fd" 에 대해 자세히 설명해 주 시겠습니까?
Janis

2
{ true >&3; } 2> /dev/null현재 환경에 영향을 미치지 않으며 포크하지 않습니다 (Bourne 쉘 제외). 그 의미 (exec 1>&3) 2>&-는 읽기 전용 모드로 개방 전략 중 true를 반환합니다.
Stéphane Chazelas 2016 년

1
exec특수 내장 기능은 쉘이 실패하면 bash를 종료합니다 (bash의 경우 POSIX 호환 모드에서만). command exec방지합니다. true특별한 내장이 아닙니다. 참고 execcommand exec현재의 환경에 영향을 미치지 않는다 (즉, 내가 말한 이유의 당신이 그것에 표준 출력을 리디렉션 할 경우 ).
Stéphane Chazelas

-1

반복적으로 사용하기 위해 낮은 포크 솔루션에 관심이 있다면이 기능을 제안합니다.

checkfd () {
    exec 2> / dev / null
    exec> & 3이면; 그때
        exec 1> / dev / tty
        에코 "fd3 OK"
    그밖에
        에코 "fd3 KO"
    fi
    exec 2> / dev / tty
}

그리고 여기에 그것이 생산하는 것이 있습니다 zsh:

$ checkfd            
fd3 KO
$ checkfd 3> / dev / null
fd3 OK
$

대부분의 쉘 exec >&3은 3이 열려 있지 않으면 쉘을 죽입니다.
mikeserv

적어도 그것은 노력 zsh하고 bash있습니다. 실패로 exec인한 쉘을 제공 할 수 exit있습니까?
dan

네. 에서 bash수행 set -o posix하고 다시 시도하십시오. 에서 zsh... 나는 그것이 ENV var에 설정의 문제라고 생각 POSIX_BUILTINS하지 않은 널 (null) 값으로 -하지만 난 아무 렇게 잊어 버려. 어쨌든 zshPOSIX 준수를 시도하는 쉘이 아니기 때문에 확실히 비표준입니다. 무엇을 그 껍질 피하다 호환성 모두 어떤 생각이 편리합니다.
mikeserv 2016 년

일반 Bourne 쉘에서도 작업 중입니다.
dan

bash에서 set -o posix시도해보십시오.
dan

-1

이것은 매우 쉬운 것처럼 보입니다 (댓글 참조).

[ -r /proc/$$/fd/$FD ] && echo "File descriptor $FD is readable"
[ -w /proc/$$/fd/$FD ] && echo "File descriptor $FD is writable"

추가로 ... [-r file] 테스트는 실제로 데이터 읽기 대기 중인지 여부를 나타내지 않습니다 (/ dev / null이이 테스트를 통과합니다 (주석 참조)).

[ -r /proc/$$/fd/4 ] \
  && [ read -t 0.0001 -N 0 <&4 ] \
  && echo "Data is waiting to be read from file descriptor 4"

시간 종료 인수 (read -t)에 작은 숫자가 필요하거나 계산이 필요한 데이터가 누락 될 수 있습니다. 읽을 수있는 테스트 ([-r file])가 필요하거나 파일을 읽을 수없는 경우 읽기 명령이 실행됩니다. 바이트 수가 0이므로 (읽기 -N 0) 실제로 데이터를 읽지 않습니다.


Linux 시스템 /proc/<pid>/fdinfo/<fd>을 사용하려는 경우 열려있는 모든 파일 모드가 아래에 나열되어 있습니다 . 여기를flags: 참조 하십시오 . 왜 두 번째 부분 (눈부신 실수를 고친 후에도) : read -t .1 -N0 <&4fd 4에서 읽을 데이터가 있는지 알 수 없습니다 4</dev/null.
mosvy

물론 [ -r /proc/$$/fd/$FD ]파일 디스크립터 $FD를 읽을 수 있는지 여부를 알려주지는 않지만 다른 파일 디스크립터를 사용하여 파일을 연 파일을 다시 열 수 있다면 다음과 같이 읽을 수 있습니다 .exec 7>/tmp/foo; [ -r /proc/$$/fd/7 ] && echo fd 7 can be read from && cat <&7
mosvy

-1

질문은 꽤 오래되었지만 어쨌든 내장을 사용하지 않는 이유는 무엇입니까?

for i in {0..5} ; do if [ -t $i ]; then echo "$i is a valid FD"; else echo "$i is INVALID FD"; fi; done

산출:

0 is a valid FD
1 is a valid FD
2 is a valid FD
3 is INVALID FD
4 is INVALID FD
5 is INVALID FD

따라서 질문에 대답하려면 다음을 제안하십시오.

if [ -t 3 ]; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

-t파일 디스크립터가 유효한지 테스트하지 않지만 tty에 연결되어 있는지 확인합니다. echo yup |스크립트 앞에 a 를 붙이면 0 is INVALID FD실제로는 매우 유효한 fd 파이프입니다.
mosvy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.