추가 파일 설명자를 언제 사용 하시겠습니까?


74

파일 설명자를 만들고 출력을 리디렉션 할 수 있다는 것을 알고 있습니다. 예 :

exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.

그러나 파일 디스크립터없이 동일한 작업을 수행 할 수 있습니다.

FILE=/tmp/foo
echo a > "$FILE"

추가 파일 설명자를 사용해야하는 좋은 예를 찾고 있습니다.

답변:


50

대부분의 명령에는 단일 입력 채널 (표준 입력, 파일 디스크립터 0)과 단일 출력 채널 (표준 출력, 파일 디스크립터 1)이 있거나 직접 열 수있는 여러 파일에서 작동하므로 파일 이름을 전달합니다. (표준 오류 (fd 2)와 더불어 일반적으로 사용자에게 모든 것을 필터링합니다.) 그러나 때때로 여러 소스 또는 여러 대상에서 필터로 작동하는 명령을 사용하는 것이 편리합니다. 예를 들어, 파일의 홀수 라인과 짝수 라인을 구분하는 간단한 스크립트가 있습니다.

while IFS= read -r line; do
  printf '%s\n' "$line"
  if IFS= read -r line; then printf '%s\n' "$line" >&3; fi
done >odd.txt 3>even.txt

이제 홀수 라인과 짝수 라인에 다른 필터를 적용하려고한다고 가정합니다 (다시 정리하지 마십시오. 일반적으로 쉘에서는 불가능한 다른 문제입니다). 쉘에서는 명령의 표준 출력 만 다른 명령으로 파이프 할 수 있습니다. 다른 파일 디스크립터를 파이프하려면 먼저 fd 1로 경로 재지 정해야합니다.

{ while  done | odd-filter >filtered-odd.txt; } 3>&1 | even-filter >filtered-even.txt

또 다른 간단한 사용 사례는 명령의 오류 출력을 필터링하는 것 입니다.

exec M>&N스크립트의 나머지 부분에 대해 파일 설명자를 다른 것으로 설명합니다 (또는 다른 명령이 파일 설명자를 다시 변경할 때까지). exec M>&N와 (과)의 기능이 일부 겹칩니다 somecommand M>&N. exec형태는 중첩 될 필요가 없다는 더 강력하다 :

exec 8<&0 9>&1
exec >output12
command1
exec <input23
command2
exec >&9
command3
exec <&8

관심있는 다른 예 :

그리고 더 많은 예를 들어 :

추신 : 이것은 fd 3을 통한 경로 재 지정을 사용하는 사이트 에서 가장 많이 게시 된 게시물 의 저자가 보낸 놀라운 질문입니다 !


차라리 "대부분의 명령에는 단일 또는 이중 출력 채널이 있습니다 -stdout (fd 1) 및 매우 자주 stderr (fd 2)"입니다.
rozcietrzewiacz

또한, 왜 당신이 사용하는지 설명 할 수 while IFS= read -r line;있습니까? 내가 보는 것처럼 IFS는 하나의 변수 ( line ) 에만 값을 할당하기 때문에 여기서 효과가 없습니다 . 이 질문을보십시오.
rozcietrzewiacz

@rozcietrzewiacz 나는 stderr에 대해 언급 IFS했으며 단일 변수로 읽는 경우에도 왜 차이 가 나는지에 대한 대답의 첫 부분을 봅니다 (앞 공백을 유지해야 함).
Gilles

당신도 똑같이 할 수 sed -ne 'w odd.txt' -e 'n;w even.txt'없습니까?
와일드 카드

1
@Wildcard 다른 도구들도 마찬가지입니다. 그러나이 답변의 목표는 쉘의 리디렉션을 설명하는 것이 었습니다.
Gilles

13

다음은 여분의 FD를 bash 스크립트 대화 제어로 사용하는 예입니다.

#!/bin/bash

log() {
    echo $* >&3
}
info() {
    echo $* >&4
}
err() {
    echo $* >&2
}
debug() {
    echo $* >&5
}

VERBOSE=1

while [[ $# -gt 0 ]]; do
    ARG=$1
    shift
    case $ARG in
        "-vv")
            VERBOSE=3
        ;;
        "-v")
            VERBOSE=2
        ;;
        "-q")
            VERBOSE=0
        ;;
        # More flags
        *)
        echo -n
        # Linear args
        ;;
    esac
done

for i in 1 2 3; do
    fd=$(expr 2 + $i)
    if [[ $VERBOSE -ge $i ]]; then
        eval "exec $fd>&1"
    else
        eval "exec $fd> /dev/null"
    fi
done

err "This will _always_ show up."
log "This is normally displayed, but can be prevented with -q"
info "This will only show up if -v is passed"
debug "This will show up for -vv"

8

명명 된 파이프 (fifos)와 관련하여 추가 파일 디스크립터를 사용하면 비 블로킹 파이핑 동작을 사용할 수 있습니다.

(
rm -f fifo
mkfifo fifo
exec 3<fifo   # open fifo for reading
trap "exit" 1 2 3 15
exec cat fifo | nl
) &
bpid=$!

(
exec 3>fifo  # open fifo for writing
trap "exit" 1 2 3 15
while true;
do
    echo "blah" > fifo
done
)
#kill -TERM $bpid

참조 : 명명 된 파이프가 스크립트에서 조기에 종료됩니까?


1
당신은 내 오래된 질문 중 하나를 파헤 쳤습니다 :) 차드가 맞습니다. 경쟁 조건에 처하게됩니다.
n0pe

6

추가 파일 디스크립터는 변수에서 stdout을 잡으려고하지만 여전히 bash 스크립트 사용자 인터페이스와 같이 화면에 쓰려고 할 때 유용합니다.

arg1 string to echo 
arg2 flag 0,1 print or not print to 3rd fd stdout descriptor   
function ecko3 {  
if [ "$2" -eq 1 ]; then 
    exec 3>$(tty) 
    echo -en "$1" | tee >(cat - >&3)
    exec 3>&- 
else 
    echo -en "$1"  
fi 
}

2
나는 이것이 새로운 대답이 아니라는 것을 알고 있지만, 그것이 무엇을하고 있는지보고 누군가가 사용중인이 기능의 예를 추가하면 도움이 될 것이라고 생각했습니다. 이 경우 명령-df. dl.dropboxusercontent.com/u/54584985/mytest_redirect
Joe


1

예 : flock을 사용하여 파일 잠금과 함께 스크립트를 직렬로 실행

한 가지 예는 파일 잠금을 사용하여 스크립트가 시스템 전체에서 강제로 실행되도록하는 것입니다. 같은 종류의 두 스크립트가 같은 파일에서 작동하지 않게하려는 경우에 유용합니다. 그렇지 않으면 두 스크립트가 서로 간섭하여 데이터가 손상 될 수 있습니다.

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#open file descriptor 3 for writing
exec 3> /tmp/file.lock

#create an exclusive lock on the file using file descriptor 3
#exit if lock could not be obtained
flock -n 3

#execute serial code

#remove the file while the lock is still obtained
rm -f /tmp/file.lock

#close the open file handle which releases the file lock and disk space
exec 3>&-

잠금 및 잠금 해제를 정의하여 기능적으로 무리 사용

이 잠금 / 잠금 해제 논리를 재사용 가능한 기능으로 랩핑 할 수도 있습니다. 다음 trap셸 내장 은 스크립트가 종료 될 때 파일 오류를 자동으로 해제합니다 (오류 또는 성공). trap파일 잠금을 정리하는 데 도움이됩니다. 경로 /tmp/file.lock는 여러 스크립트가 잠글 수 있도록 하드 코딩 된 경로 여야합니다.

# obtain a file lock and automatically unlock it when the script exits
function lock() {
  exec 3> /tmp/file.lock
  flock -n 3 && trap unlock EXIT
}

# release the file lock so another program can obtain the lock
function unlock() {
  # only delete if the file descriptor 3 is open
  if { >&3 ; } &> /dev/null; then
    rm -f /tmp/file.lock
  fi
  #close the file handle which releases the file lock
  exec 3>&-
}

unlock위 의 논리는 잠금이 해제되기 전에 파일을 삭제하는 것입니다. 이런 식으로 잠금 파일을 정리합니다. 파일이 삭제되었으므로이 프로그램의 다른 인스턴스가 파일 잠금을 얻을 수 있습니다.

스크립트에서 잠금 및 잠금 해제 기능 사용

다음 예제와 같이 스크립트에서 사용할 수 있습니다.

#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e

#try to lock (else exit because of non-zero exit code)
lock

#system-wide serial locked code

unlock

#non-serial code

코드가 잠길 때까지 기다리려면 다음과 같이 스크립트를 조정할 수 있습니다.

set -e

#wait for lock to be successfully obtained
while ! lock 2> /dev/null; do
  sleep .1
done

#system-wide serial locked code

unlock

#non-serial code

0

구체적인 예로서, 방금 하위 명령의 타이밍 정보가 필요한 스크립트를 작성했습니다. 추가 파일 설명자를 사용하면 time하위 명령의 stdout 또는 stderr를 중단하지 않고 명령의 stderr 을 캡처 할 수있었습니다 .

(time ls -9 2>&3) 3>&2 2> time.txt

이것이하는 것은 point ls의 stderr에서 fd 3, fd 3에서 스크립트의 stderr, point time의 stderr를 파일로 지정하는 것입니다. 스크립트가 실행될 때, stdout 및 stderr는 부속 명령과 동일하며 평소와 같이 경로 재 지정 될 수 있습니다. time의 출력 만 파일로 리디렉션됩니다.

$ echo '(time ls my-example-script.sh missing-file 2>&3) 3>&2 2> time.txt' > my-example-script.sh
$ chmod +x my-example-script.sh 
$ ./my-example-script.sh 
ls: missing-file: No such file or directory
my-example-script.sh
$ ./my-example-script.sh > /dev/null
ls: missing-file: No such file or directory
$ ./my-example-script.sh 2> /dev/null
my-example-script.sh
$ cat time.txt

real    0m0.002s
user    0m0.001s
sys 0m0.001s
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.