![텍스트 파일을 수정하면 변수 "line"을 읽는 bash while 루프가 자동으로 업데이트됩니까?](https://linux55.com/image/182823/%ED%85%8D%EC%8A%A4%ED%8A%B8%20%ED%8C%8C%EC%9D%BC%EC%9D%84%20%EC%88%98%EC%A0%95%ED%95%98%EB%A9%B4%20%EB%B3%80%EC%88%98%20%22line%22%EC%9D%84%20%EC%9D%BD%EB%8A%94%20bash%20while%20%EB%A3%A8%ED%94%84%EA%B0%80%20%EC%9E%90%EB%8F%99%EC%9C%BC%EB%A1%9C%20%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EB%90%A9%EB%8B%88%EA%B9%8C%3F.png)
예를 들어 "Hello"가 포함된 hello.txt라는 텍스트 파일이 있습니다. 읽을 수 있는 while 루프가 있는 경우:
while IFS= read -r line
do
echo $line > hello.txt # whatever that inserts/ edits something in the text file
done < hello.txt
while 루프 읽기가 변경되거나 무한 루프가 발생합니까? 아니면 while 루프가 텍스트 파일의 변경 사항을 읽지 못합니까? 업데이트/수정된 텍스트 파일을 강제로 읽도록 할 수 있나요?
답변1
루프의 입력과 read
출력이 동일한 파일에 연결됩니다. 그렇습니다.echo
이 특정한 경우에는 출력 리디렉션을 설정할 때마다 echo
개별 호출마다 파일이 잘립니다. 입력의 읽기 위치는 마지막 읽기 라인 바로 뒤의 원래 위치로 유지됩니다. 여기서는 정확히 동일한 줄을 인쇄하므로 읽기 위치는 새 EOF에 있고 루프는 한 번의 반복 후에 종료됩니다. 파일의 원래 첫 번째 줄을 파일의 유일한 내용으로 유지합니다.
그러나 더 긴 텍스트를 출력하는 경우 루프는 이전에 출력된 내용의 일부를 읽을 수 있습니다.
예를 들어 고려하십시오.
$ cat hello.txt
abc
def
ghi
$ cat test.sh
while IFS= read -r line
do
echo "$line" # to terminal, to see what is read
echo "something something $line" > "$1"
done < "$1"
이제 스크립트를 실행하면 hello.txt
다음 줄이 남습니다.
something something g something abc
첫 번째 라인 뒤에는 파일이 포함되어 something something abc
있으며 읽기 위치는 첫 번째 라인에 있습니다 t
( abc
및 개행 문자를 읽기 때문입니다). 읽기 thing something abc
및 인쇄를 반복합니다 something something thing something abc
(파일 자르기). 읽기 위치는 여전히 중간에 있으므로 최종적으로 EOF에 도달할 때까지 루프가 계속 반복됩니다.
read
특히 읽기 위치는 정확히 줄 끝에서 유지됩니다 . 이는 예를 들어 (표준 준수) 와 동일 head -n1
하지만 다른 많은 유틸리티는 전체 블록을 읽고 읽기 위치를 더 짧은 입력을 위해 원본 파일의 끝에 남겨 둡니다. 여기서 결과가 변경됩니다.
반면에 출력 리디렉션을 로 바꾸면 >> file
모든 쓰기가 파일에 추가되고 읽기 위치는 결코 끝에 도달하지 않습니다(루프는 반복당 한 줄만 읽고 쓰기 때문에). 무한 루프가 발생합니다.
다시 말하지만 만약 우리가제거하다루프 내부의 출력 파일은 리디렉션 전에 모든 것이 변경됩니다.
while IFS= read -r line
do
rm "$1"
echo "$line" # to terminal, to see what is read
echo "something something $line" > "$1"
done < "$1"
이제 더 이상 이름이 없기 때문에 출력 리디렉션에서 동일한 파일에 액세스할 수 없습니다. 새 파일이 생성됩니다. 읽기 파일 핸들은 여전히 파일에 연결되어 있습니다. 더 이상 열려 있지 않은 경우에만 결국 삭제됩니다. 원본 버전에서 이것을 실행하면 hello.txt
새로운 버전이 남습니다 hello.txt
.
something something ghi
이제 출력 파일이 입력과 독립적이므로 루프는 입력을 끝까지 읽습니다. 각 반복마다 삭제되고 다시 생성됩니다.
루프 반복마다 파일을 삭제하는 것은 그다지 유용하지 않지만, 한 번만 삭제된 경우 리디렉션을 약간 이동해야 합니다. 그러면 전체 파일이 반복되고 모든 줄에 다음이 붙은 동일한 이름의 새 파일이 남습니다 something something
.
exec < "$1" # input redirection for the whole shell script
rm "$1"
exec > "$1" # symmetrically, output, creating a new file
# with the same name
while IFS= read -r line
do
echo "$line" >&2 # to terminal via stderr, to see what is read
echo "something something $line"
done
답변2
이를 시험해보고 몇 가지 디버깅 정보를 살펴보겠습니다. 저는 커널 호출에 익숙하지 않지만 경험이 많은 사람이 실수를 수정할 수 있기를 바랍니다.
$ cd "$(mktemp --directory)"
$ cat > test.bash <<'EOF'
> while IFS= read -r line
> do
> echo "$line" > hello.txt
> done < hello.txt
> EOF
$ echo foo > hello.txt
$ strace bash --noprofile --norc test.bash
[skipping the script setup for clarity, and annotating the rest]
# Open hello.txt for reading as file descriptor 3
openat(AT_FDCWD, "hello.txt", O_RDONLY) = 3
fcntl(0, F_GETFD) = 0
fcntl(0, F_DUPFD, 10) = 10
fcntl(0, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
# Duplicate FD 3 as FD 0
dup2(3, 0) = 0
# Close FD 3
close(3) = 0
ioctl(0, TCGETS, 0x7ffc7a087960) = -1 ENOTTY (Inappropriate ioctl for device)
# Go to start of FD 0
lseek(0, 0, SEEK_CUR) = 0
# Read up to max 128 bytes from FD 0. Four bytes read, which is the full file.
# `read` strips the newline, so $line ends up containing just "foo"
read(0, "foo\n", 128) = 4
# Open hello.txt for writing as FD 3
openat(AT_FDCWD, "hello.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 11
fcntl(1, F_GETFD) = 0
fcntl(11, F_SETFD, FD_CLOEXEC) = 0
# Duplicate FD 3 as FD 1
dup2(3, 1) = 1
# Close FD 3
close(3) = 0
fstat(1, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
# Write what we read plus a newline to FD 1
write(1, "foo\n", 4) = 4
dup2(11, 1) = 1
fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(11) = 0
ioctl(0, TCGETS, 0x7ffc7a087960) = -1 ENOTTY (Inappropriate ioctl for device)
# Go to current position in FD 0 (index 4, as returned by the previous `read`)
lseek(0, 0, SEEK_CUR) = 4
# Try to read again from FD 0. Encounters end of file (return value 0).
read(0, "", 128) = 0
# Shut down
dup2(10, 0) = 0
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
read(255, "", 129) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
exit_group(0) = ?
기본적으로 EOF가 발생하면 루프가 종료 read
됩니다 . while
스크립트를 교체하면 echo "$line" > hello.txt
실행될 때마다 파일이 끝나기 전에 더 많은 내용이 있기 echo "$line" >> hello.txt
때문에 저장소가 가득 찰 때까지 실행됩니다 .read