셸 스크립트 읽기 마지막 줄이 누락됨
저는...제가 통찰력을 얻고 싶었던 bash shell 스크립트와 관련된 이상한 문제.
저희 팀은 파일의 행을 반복하고 각 행의 내용을 확인하는 스크립트를 작성하고 있습니다.여러 스크립트를 함께 배열하는 자동화된 프로세스를 통해 실행할 때 마지막 줄이 보이지 않는 버그가 있었습니다.
줄 할 때 은 " 의줄저위반데코드는이사름되용는된파장복일하서에저▁stored▁in▁used()▁the이코▁code름▁("에 저장됨)DATAFILE
이었다
cat "$DATAFILE" | while read line
명령줄에서 스크립트를 실행하면 마지막 줄을 포함하여 파일의 모든 줄을 볼 수 있습니다.그러나 자동화된 프로세스(문제의 스크립트 바로 전에 DATAFILE을 생성하는 스크립트를 실행)에 의해 실행되는 경우에는 마지막 줄이 표시되지 않습니다.
코드를 업데이트하여 다음을 사용하여 회선을 반복했고 문제가 해결되었습니다.
for line in `cat "$DATAFILE"`
참고: DATAFILE에는 파일 끝에 새 줄이 기록되지 않습니다.
제 질문은 두 부분입니다...왜 마지막 줄이 원래 코드에 표시되지 않고, 왜 변경이 차이를 만드는 이유는 무엇입니까?
마지막 줄이 보이지 않는 이유에 대해 생각해 낼 수 있다고 생각했습니다.
- 파일을 쓰는 이전 프로세스는 파일 설명자를 닫기 위해 종료 프로세스에 의존했습니다.
- 문제 스크립트가 파일을 시작하고 열었을 때 이전 프로세스가 "종료"되었지만 시스템이 파일 설명자를 자동으로 닫을 만큼 충분히 "종료/정리"되지 않았습니다.
즉, 셸 스크립트에 두 개의 명령이 있는 경우 스크립트가 두 번째 명령을 실행할 때까지 첫 번째 명령을 완전히 종료해야 하는 것 같습니다.
질문에 대한 통찰력, 특히 첫 번째 질문에 감사드립니다.
C 표준은 텍스트 파일이 새 줄로 끝나야 하며 그렇지 않으면 마지막 새 줄 이후의 데이터가 제대로 읽히지 않을 수 있다고 말합니다.
ISO/IEC 9899:2011 §7.21.2 스트림
텍스트 스트림은 줄로 구성된 문자의 순서 순서 순서이며, 각 줄은 0개 이상의 문자와 끝 줄 문자로 구성됩니다.마지막 줄에 종료 새 줄 문자가 필요한지 여부는 구현 정의된 것입니다.호스트 환경에서 텍스트를 나타내는 다른 규칙을 준수하기 위해 입력 및 출력 시 문자를 추가, 변경 또는 삭제해야 할 수 있습니다.따라서 스트림의 문자와 외부 표현의 문자 사이에 일대일 대응이 있을 필요는 없습니다.텍스트 스트림에서 읽은 데이터는 데이터가 인쇄 문자와 제어 문자 수평 탭 및 줄 바꿈으로만 구성되고, 줄 바꿈 문자는 공백 문자 바로 앞에 없고, 마지막 문자는 줄 바꿈 문자인 경우에만 해당 스트림에 이전에 기록된 데이터와 동일하게 비교됩니다.읽기 시작할 때 줄 바꿈 문자 바로 앞에 쓰여지는 공백 문자의 표시 여부가 구현 정의됩니다.
새이 문제를 일으킬 줄은 예상하지 못했을 입니다.bash
셸), 가능한 인 것 $
는 이 출력의 프롬프트입니다).
$ echo xxx\\c
xxx$ { echo abc; echo def; echo ghi; echo xxx\\c; } > y
$ cat y
abc
def
ghi
xxx$
$ while read line; do echo $line; done < y
abc
def
ghi
$ bash -c 'while read line; do echo $line; done < y'
abc
def
ghi
$ ksh -c 'while read line; do echo $line; done < y'
abc
def
ghi
$ zsh -c 'while read line; do echo $line; done < y'
abc
def
ghi
$ for line in $(<y); do echo $line; done # Preferred notation in bash
abc
def
ghi
xxx
$ for line in $(cat y); do echo $line; done # UUOC Award pending
abc
def
ghi
xxx
$
또한 이에 국한되지 않습니다.bash
콘 껍질(ksh
및 ) 및zsh
그런 식으로 행동합니다.저는 살고 있습니다. 문제를 제기해 주셔서 감사합니다.
바와 같이, 위의코드입바같와이증된서에,같,cat
명령은 전체 파일을 읽습니다. 그for line in `cat $DATAFILE`
매개 변수는 모든 출력을 수집하고 공백의 임의 시퀀스를 하나의 공백으로 바꿉니다(파일의 각 행에는 공백이 포함되지 않는다고 결론짓습니다).
Mac OS X 10.7.5에서 테스트되었습니다.
POSIX는 뭐라고 합니까?
POSIX 명령 사양은 다음과 같습니다.
읽기 유틸리티는 표준 입력에서 한 줄을 읽어야 합니다.
기적으로, 그지않경우가 아닌
-r
옵션을 지정하면, <백슬래시>가 이스케이프 문자로 작동합니다.이스케이프되지 않은 <백슬래시>는 <새줄>을 제외하고 다음 문자의 리터럴 값을 보존합니다.<새로운 줄>이 <백슬래시> 뒤에 오는 경우, 읽기 유틸리티는 이를 줄 연속으로 해석해야 합니다.및 <백슬래시>는<newline>
입력을 필드로 분할하기 전에 제거해야 합니다.입력을 필드로 분할한 후 이스케이프되지 않은 다른 모든 <백슬래시> 문자가 제거됩니다., 는 <으로 때 . 단, "backslash" <new line>은 "backslash> <new line>입니다.
-r
옵션이 지정되었습니다.종료되는 <new line>(있는 경우)은 입력에서 제거되어야 하며 결과는 파라미터 확장의 결과를 위해 셸에서와 같이 필드로 분할되어야 합니다(필드 분할 참조); [...]
'(있는 경우)'(따옴표에 강조 표시)에 유의하십시오!제가 보기에 새로운 라인이 없다면, 여전히 결과를 읽어야 할 것 같습니다.다른 한편으로는 다음과 같은 내용도 있습니다.
STDIN
표준 입력은 텍스트 파일이어야 합니다.
그런 다음 새 줄로 끝나지 않는 파일이 텍스트 파일인지 여부에 대한 논쟁으로 돌아갑니다.
그러나 동일한 페이지의 근거 문서는 다음과 같습니다.
<newline 파일이 , 이 경우 는 <newline>(새줄일 때 됩니다.
-r
not line으로 끝나지 수 . not used는 <new line>으로 끝날 수 있습니다.입력 파일의 마지막 줄이 <backslash> <newline>으로 끝나는 경우 이 문제가 발생합니다.이러한 이유로 설명의 "종료 <새로운 라인>(있는 경우)은 입력에서 제거되어야 합니다"에서 "있는 경우"가 사용됩니다.표준 입력이 텍스트 파일이어야 한다는 요구사항을 완화하는 것은 아닙니다.
이러한 근거는 텍스트 파일이 새 줄로 끝나야 한다는 것을 의미해야 합니다.
텍스트 파일의 POSIX 정의는 다음과 같습니다.
3.395 텍스트 파일
0개 이상의 줄로 구성된 문자가 들어 있는 파일입니다.줄에 NUL 문자가 포함되어 있지 않으며 <newline> 문자를 포함하여 길이가 {LINE_MAX}바이트를 초과할 수 없습니다.POSIX.1-2008은 텍스트 파일과 이진 파일을 구별하지 않지만(ISOC 표준 참조) 많은 유틸리티는 텍스트 파일에서 작동할 때 예측 가능하거나 의미 있는 출력만 생성합니다.이러한 제한이 있는 표준 유틸리티는 항상 STDIN 또는 INPUT FILES 섹션에 "텍스트 파일"을 지정합니다.
이것은 '<새로운 줄로 끝나는 것>'을 직접적으로 규정하지는 않지만, C 표준을 따르며 "0개 이상의 줄로 구성된 문자를 포함하는 파일"이라고 하며, 우리가 POSIX의 "줄" 정의를 볼 때 다음과 같이 말합니다.
3.206 라인
0개 이상의 비<newline> 문자와 종료 <newline> 문자로 구성된 시퀀스입니다.
따라서 POSIX 정의에 따라 파일은 줄로 구성되어 있고 각 줄은 줄 끝에 있는 줄로 끝나야 하기 때문에 파일은 끝에 있는 줄로 끝나야 합니다.
'새로운 터미널이 없는' 문제의 해결책
Gordon Davisson의 대답을 메모합니다.간단한 검정을 통해 그의 관측치가 정확하다는 것을 알 수 있습니다.
$ while read line; do echo $line; done < y; echo $line
abc
def
ghi
xxx
$
따라서 그의 기술은 다음과 같습니다.
while read line || [ -n "$line" ]; do echo $line; done < y
또는:
cat y | while read line || [ -n "$line" ]; do echo $line; done
(적어도 내 컴퓨터에서는) 끝에 줄이 새로 생기지 않은 파일에 대해 작동합니다.
셸이 입력의 마지막 세그먼트(새 줄로 끝나지 않기 때문에 줄이라고 할 수 없음)를 떨어뜨리는 것은 여전히 놀랍지만, POSIX에는 그렇게 하기에 충분한 정당성이 있을 수 있습니다.텍스트 파일이 새 줄로 끝나는 텍스트 파일인지 확인하는 것이 가장 좋습니다.
읽기 명령에 대한 POSIX 사양에 따르면 "파일 종료가 감지되었거나 오류가 발생한 경우" 0이 아닌 상태를 반환해야 합니다.EOF가 마지막 "라인"을 읽을 때 감지되므로 설정됩니다.$line
그런 다음 오류 상태를 반환하고 오류 상태로 인해 해당 마지막 "라인"에서 루프가 실행되지 않습니다.하거나 읽기 루프를 실행합니다. 읽기 명령이 성공하면 루프를 실행하거나 읽기 시작한 항목이 있으면 루프를 실행합니다.$line
.
while read line || [ -n "$line" ]; do
일부 추가 정보 추가:
- 사용할 필요가 없습니다.
cat
루프를 반복하여while ...;do something;done<file
충분합니다. - 행을읽않음이 있는 은 읽지
for
.
while 루프를 사용하여 라인을 읽을 때:
- 을 합니다.
IFS
올바르게(그렇지 않으면 들여쓰기가 손실될 수 있습니다). - 읽기와 함께 거의 항상 -r 옵션을 사용해야 합니다.
위의 요구 사항을 충족하면 적절한 동안 루프는 다음과 같이 나타납니다.
while IFS= read -r line; do
...
done <file
그리고 끝에 새 줄이 없는 파일과 함께 작동하도록 하려면(여기서 솔루션을 다시 게시):
while IFS= read -r line || [ -n "$line" ]; do
echo "$line"
done <file
는사용을 사용합니다.grep
while 루프while 프루:
while IFS= read -r line; do
echo "$line"
done < <(grep "" file)
해결 방법으로 텍스트 파일을 읽기 전에 파일에 새 줄을 추가할 수 있습니다.
echo -e "\n" >> $file_path
이렇게 하면 이전에 파일에 있던 모든 행을 읽을 수 있습니다.이스케이프 시퀀스를 해석할 수 있도록 -e 인수를 에코로 전달해야 합니다.https://superuser.com/questions/313938/shell-script-echo-new-line-to-file
명령줄에서 테스트했습니다.
# create dummy file. last line doesn't end with newline
printf "%i\n%i\nNo-newline-here" >testing
첫 번째 폼으로 테스트(루프 도중 파이프 연결)
cat testing | while read line; do echo $line; done
이것은 마지막 줄을 놓칩니다. 이것은 다음 이후로 말이 됩니다.read
새 줄로 끝나는 입력만 가져옵니다.
두 번째 양식으로 테스트(명령 대체)
for line in `cat testbed1` ; do echo $line; done
이것도 마지막 줄을 받습니다.
read
only gets input if it's terminated by newline, that's why you miss the last line.
반면에, 두 번째 형태에서는.
`cat testing`
의 형태로 확장됩니다.
line1\nline2\n...lineM
그것은 셸에 의해 IFS를 사용하여 여러 필드로 분리되어, 그래서 당신은.
line1 line2 line3 ... lineM
그래서 당신은 여전히 마지막 줄을 알고 있습니다.
p/s: 이해가 안 되는 것은 어떻게 첫 번째 양식을 작동시키느냐는 것입니다.
파일의 마지막 줄을 일치시키는 데 사용되며, 파일이 없는 경우 새 줄을 추가하고 파일을 인라인으로 교체하도록 합니다.
sed -i '' -e '$a\' file
이 스택 교환 링크의 코드입니다.
빈를 단따: 다추음다니가에습에 추가했습니다.-i ''
에서는, 하면냐왜, 도어는 OS X서에,-i
사용하고 있었습니다.-e
백업 파일의 파일 확장자로 사용할 수 있습니다.저는 원래 게시물에 기꺼이 댓글을 달았지만 50점이 부족했습니다.아마도 이것은 저에게 이 실에서 몇 개를 얻을 수 있을 것입니다, 감사합니다.
저도 비슷한 문제가 있었습니다.저는 파일을 고양이처럼 분류한 다음 결과를 'var1var2var3를 읽는 동안'으로 분류했습니다.i: cat $FILE|sort-k3|Count IP Name do "do" 아래의 작업은 $Name 필드에서 변경되는 데이터를 식별하고 변경 또는 변경 없이 $Count 합계를 수행하거나 보고서에 요약된 줄을 인쇄하는 if 문이었습니다.또한 보고서에 인쇄할 마지막 줄을 얻을 수 없는 문제에 부딪혔습니다.저는 cat/sort를 새 파일로 리디렉션하고 새 행을 새 파일로 반향한 다음 새 파일에서 "읽는 동안 IP 이름"을 실행하여 결과를 성공적으로 얻었습니다.i: cat $FILE|sort -k3 > NEWFILE 에코 "\n" > NEWFILE cat NEWFILE | count IP Name do 때때로 간단하고 품위 없는 것이 가장 좋은 방법입니다.
언급URL : https://stackoverflow.com/questions/12916352/shell-script-read-missing-last-line
'programing' 카테고리의 다른 글
Eclipse 텍스트 커서가 변경되었으며 편집기가 다르게 작동함 (0) | 2023.06.02 |
---|---|
Active Directory에서 사용자 그룹을 가져오는 방법은 무엇입니까?(c#, asp.net ) (0) | 2023.06.02 |
iPhone Simulator 캐시를 삭제하는 것보다 더 빠르고 더 나은 방법이 있습니까? (0) | 2023.05.28 |
C# "As"에 해당하는 VB.NET (0) | 2023.05.28 |
Windows 및 Linux 디렉터리 이름에서 금지된 문자는 무엇입니까? (0) | 2023.05.28 |