파일 설명자 읽기에 실패했습니다.

파일 설명자 읽기에 실패했습니다.

이 질문은 파일 설명자를 읽고 쓰는 것에 관한 것입니다. 다음 예를 참조하세요.

#!/bin/sh

file='somefile'

# open fd 3 rw
exec 3<> "$file"

# write something to fd 3
printf "%s\n%s\n" "foo" "bar" >&3

# reading from fd 3 works
cat "/proc/$$/fd/3"

# only works if the printf line is removed
cat <&3

exit 0

스크립트는 다음을 출력합니다.

foo
bar

예상 출력:

foo
bar
foo
bar

파일 설명자를 열고 쓰는 데 성공했습니다. 독서도 마찬가지다 proc/$$/fd/3. 하지만 이것은휴대가 쉽지 않음. cat <&3아무것도 출력하지 않습니다. 그러나 파일 설명자가 작성되지 않은 경우(예: 해당 줄의 주석 처리 제거 ) printf작동합니다 .

작동하지 않는 이유는 무엇 cat <&3이며 휴대용 파일 설명자(POSIX 쉘)에서 전체 내용을 어떻게 읽을 수 있습니까?

답변1

cat <&3파일의 끝에 도달할 때까지 파일에서 읽어오는 작업을 정확히 수행합니다. 호출하면 파일 설명자의 파일 위치는 마지막으로 떠난 위치, 즉 파일의 끝입니다. (읽기와 쓰기를 위한 별도의 위치가 아닌 하나의 파일 위치가 있습니다.)

cat /proc/$$/fd/3동일한 작업을 수행하지 않습니다 cat <&3. 다른 설명자에서 동일한 파일을 엽니다. 각 파일 설명자에는 고유한 위치가 있고 파일을 읽기 위해 열 때 위치가 0으로 설정되므로 이 명령은 전체 파일을 인쇄하고 스크립트에는 영향을 주지 않습니다.

작성한 내용을 다시 읽으려면 파일을 다시 열거나 파일 설명자를 되감아야 합니다(예: 위치를 0으로 설정). POSIX 쉘과 대부분의 sh 구현에는 이를 수행하는 기본 제공 방법이 없습니다(ksh93에 하나 있습니다). 추구해야 할 유틸리티는 단 하나뿐입니다.dd, 그러나 앞으로만 탐색할 수 있습니다. (앞으로 뛰어넘을 수 있는 다른 유틸리티가 있지만 이는 도움이 되지 않습니다.)

유일한 휴대용 솔루션은 파일 이름을 기억하고 필요한 만큼 여러 번 여는 것이라고 생각합니다. 파일이 일반 파일이 아닌 경우 이전 버전을 확인하지 못할 수도 있으니 주의하세요.

답변2

#!/bin/sh

exec 3>file
exec 4<file

printf "%s\n" "foo" "bar" >&3
cat <&4

읽기 및 쓰기에 별도의 파일 설명자를 사용하면 파일에서 별도의 위치를 ​​얻을 수 있습니다. 쓰기는 읽기 위치를 변경하지 않습니다.

답변3

ksh93을 사용하면 다음을 찾을 수 있습니다.

#!/usr/bin/env ksh93
file='somefile'
exec 3<> "$file"
printf "%s\n%s\n" "foo" "bar" >&3
cat "/proc/$$/fd/3"
exec 3>#((0))
cat <&3
exit 0

나는 얻다:

foo
bar
foo
bar

예상대로.

답변4

다시 읽고 싶은 파일 설명자에 대한 작업을 수행하는 경우 여기에서 문서 본문으로 이식할 수 있습니다.

exec 4<somefile 3<<plus
$(    printf %s\\n some random lines
        cat <&4
)
plus
cat <&3

이 솔루션은 완벽하지 않을 수 있습니다. 예를 들어 여기서는 문서 파일 설명자로 찾을 수 있는 이식 가능한 방법이 없습니다. 명령 대체는 cat표준 출력에 기록된 내용에서 뒤따르는 공백 행을 제거하지만 이러한 모든 문제는 매우 간단하게 처리될 수 있습니다.

첫째, 리디렉션의 범위는 포함된 복합 명령으로 제한되므로 필요할 수 있는 반복을 처리하기 위해 루프에 중첩시키는 것은 간단한 문제입니다. 명령 하위 메뉴 자체 내에서 루프를 구현하거나 /dev/tty원하는 경우 대화형 셸을 호출하는 것도 가능합니다 . 그러나 대부분의 경우 구분된 문서를 함수 정의에 연결하는 것을 선호합니다.

fn() { : do something w/ fd3
} 3<<INPUT
$1
$(:gen some output;:maybe cat or head <stdin)
INPUT
while IFS= read -r line; do fn "$line"; done

다른 heredoc이 작성한 내용을 읽는 것도 가능합니다.

cat 4<<SAVED <<AND
$(  cat)
SAVED
$(  printf %s\\n some random lines
     cat <&4)
AND

아니면 이렇게 반복할 수도 있습니다...

until test && cat <&4
do exec 3<&4 4<<IN
$(: gen output; cat <&3)
IN
done 4<infile

물론, fn내부에서 위와 같은 것을 호출할 수도 있습니다.

후행 빈 줄 문제에 대해 -만약에이것은 문제입니다. 그러면 echo .명령 끝에 간단히 sub를 추가하고 fd를 읽으면서 마지막 줄을 제거했는지 확인하십시오 sed \$d <&"$fd".

그러나 상당히 안전하기는 하지만 셸에서 이러한 파일을 반복하는 것은 일반적으로 나쁜 생각입니다. 생성된 각 파일에는 최소한 하나의 포크가 있다는 점에 유의하세요. 쉘은 fds를 할당하고 유틸리티는 이를 처리합니다. sed또는 스크립트에서 루프를 수행 awk하고 표준 유틸리티를 사용하여 파일 데이터를 조작한 다음 쉘을 사용하여 이를 출력으로 지정합니다. 이는 일반적으로 모범 사례입니다.

관련 정보