답변:
대부분의 명령에는 단일 입력 채널 (표준 입력, 파일 디스크립터 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
관심있는 다른 예 :
그리고 더 많은 예를 들어 :
io-redirection
file-descriptors
추신 : 이것은 fd 3을 통한 경로 재 지정을 사용하는 사이트 에서 가장 많이 게시 된 게시물 의 저자가 보낸 놀라운 질문입니다 !
while IFS= read -r line;
있습니까? 내가 보는 것처럼 IFS는 하나의 변수 ( line ) 에만 값을 할당하기 때문에 여기서 효과가 없습니다 . 이 질문을보십시오.
IFS
했으며 단일 변수로 읽는 경우에도 왜 차이 가 나는지에 대한 대답의 첫 부분을 봅니다 (앞 공백을 유지해야 함).
sed -ne 'w odd.txt' -e 'n;w even.txt'
없습니까?
다음은 여분의 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"
명명 된 파이프 (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
추가 파일 디스크립터는 변수에서 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
}
추가 파일 디스크립터를 사용할 때의 또 다른 시나리오는 다음과 같습니다 (Bash에서).
env -i bash --norc # clean up environment
set +o history
read -s -p "Enter your password: " passwd
exec 3<<<"$passwd"
mycommand <&3 # cat /dev/stdin in mycommand
한 가지 예는 파일 잠금을 사용하여 스크립트가 시스템 전체에서 강제로 실행되도록하는 것입니다. 같은 종류의 두 스크립트가 같은 파일에서 작동하지 않게하려는 경우에 유용합니다. 그렇지 않으면 두 스크립트가 서로 간섭하여 데이터가 손상 될 수 있습니다.
#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
구체적인 예로서, 방금 하위 명령의 타이밍 정보가 필요한 스크립트를 작성했습니다. 추가 파일 설명자를 사용하면 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