bash로 파일에서 줄 읽기 : for vs. while


36

bash 스크립트를 사용하여 텍스트 파일을 읽고 각 줄로 무언가를하려고합니다.

그래서 다음과 같은 목록이 있습니다.

server1
server2
server3
server4

나는 while 루프를 사용하여 이것을 반복 할 수 있다고 생각했다.

while read server; do
  ssh $server "uname -a"
done < /home/kenny/list_of_servers.txt

while 루프는 1 회 실행 후 중지되므로 uname -aserver1 에서만 실행 됩니다.

그러나 고양이를 사용하는 for 루프에서는 정상적으로 작동합니다.

for server in $(cat /home/kenny/list_of_servers.txt) ; do
  ssh $server "uname -a"
done

나에게 더 당황스러운 것은 이것이 또한 작동한다는 것입니다.

while read server; do
  echo $server
done < /home/kenny/list_of_servers.txt

첫 번째 예제가 첫 번째 반복 후에 왜 중지됩니까?

답변:


25

for루프는 여기 괜찮습니다. 그러나 파일에 공백 문자 나 글 로빙 문자가 포함되지 않은 시스템 이름이 포함되어 있기 때문입니다. 쉘은 먼저 공백이있는 곳 어디에서나 명령의 출력을 분할 한 다음 각 단어를 glob 패턴으로 취급하므로 더 확장 되기 때문에 일반적으로 for x in $(cat file); do …행을 반복하지 않습니다 . 작업 하면 안전 할 수 있습니다 .filecat file\[?*for x in $(cat file)

set -f
IFS='
'
for x in $(cat file); do 

관련 독서 : 이름에 공백이있는 파일을 반복합니까? ; bash의 변수에서 한 줄씩 읽는 방법은 무엇입니까? ; while IFS= read대신에 자주 사용 IFS=; while read..됩니까? 를 사용할 때 while read행을 읽는 안전한 구문은 while IFS= read -r line; do …입니다.

이제 당신의 while read시도 에 무엇이 잘못되었는지를 살펴 봅시다 . 서버 목록 파일의 리디렉션은 전체 루프에 적용됩니다. 따라서 ssh실행될 때 표준 입력은 해당 파일에서 가져옵니다. ssh 클라이언트는 원격 애플리케이션이 표준 입력에서 읽을 수있는시기를 알 수 없습니다. 따라서 ssh 클라이언트가 일부 입력을 감지하면 해당 입력을 원격으로 보냅니다. ssh 서버는 원할 경우 해당 입력을 원격 명령에 공급할 준비가되었습니다. 귀하의 경우 원격 명령은 입력을 읽지 않으므로 데이터는 버려 지지만 클라이언트 측은 그것에 대해 아무것도 모릅니다. 입력을 읽지 echo않기 때문에 시도 echo했지만 표준 입력 만 남겨 둡니다.

이것을 피할 수있는 몇 가지 방법이 있습니다. -n옵션을 사용하여 표준 입력에서 읽지 말도록 ssh에 지시 할 수 있습니다 .

while read server; do
  ssh -n $server "uname -a"
done < /home/kenny/list_of_servers.txt

-n사실 옵션은 말한다 ssh로부터 입력을 리디렉션 /dev/null. 쉘 수준에서 그렇게 할 수 있으며 모든 명령에서 작동합니다.

while read server; do
  ssh $server "uname -a" </dev/null
done < /home/kenny/list_of_servers.txt

파일에서 ssh의 입력을 피하는 유혹 방법은 read명령에 리디렉션을 설정하는 것 while read server </home/kenny/list_of_servers.txt; do …입니다. read명령이 실행될 때마다 파일이 다시 열리 므로 파일의 첫 번째 줄을 반복해서 읽게되므로 작동하지 않습니다 . 루프가 지속되는 동안 파일이 한 번 열리도록 리디렉션이 전체 while 루프에 있어야합니다.

일반적인 해결책은 표준 입력 이외 의 파일 디스크립터 에서 루프에 입력을 제공하는 것입니다. 쉘에는 하나의 디스크립터 번호에서 다른 디스크립터 번호로 입력 및 출력을 전달하는 구성이 있습니다. 여기서는 파일 디스크립터 3에서 파일을 열고 파일 디스크립터 3에서 read명령의 표준 입력을 경로 재 지정합니다 . ssh 클라이언트는 열린 비표준 디스크립터를 무시하므로 모든 것이 좋습니다.

while read server <&3; do
  ssh $server "uname -a"
done 3</home/kenny/list_of_servers.txt

bash에서 read명령에는 다른 파일 디스크립터에서 읽을 수있는 특정 옵션이 있으므로 쓸 수 있습니다 read -u3 server.

관련 읽기 : 파일 디스크립터 및 쉘 스크립팅 ; 추가 파일 설명자를 언제 사용 하시겠습니까?


5
이 스택 교환은 serverfault와 다릅니다. 여기 사람들은 실제로 대답 할뿐만 아니라 교육하기 위해 나아갑니다. 고마워
Kenny Rasschaert

26

while아닌을for 사용해야합니다 . 이러한 루프에서 표준 입력을 삼키는 명령을 피하는 방법은 다른 파일 설명자를 사용하는 것입니다 .

while read -u 9 server; do
  ssh $server "uname -a"
done 9< /home/kenny/list_of_servers.txt

자세한 내용은 help [r]ead( 실제로 ) 다른 이유를 설명하는 기사를 참조하십시오 .


1
흥미롭게도, 전에는 파일 디스크립터를 사용해 본 적이 없습니다. 뭐라고합니까 -u플래그는 무엇입니까? 어떤 맨 페이지에서 찾아야할지 모르겠습니다.
케니 Rasschaert

read 명령은 쉘 bash의 일부이므로 bash의 맨 페이지에있는 "SHELL BUILTIN COMMANDS"섹션에 있습니다. "-u fd 파일 디스크립터 fd에서 입력을 읽습니다." 이 단락에서
f4m8

6
또는 help read- helpman쉘 내장에 해당하는 페이지 를 가져 오는 명령 입니다.
l0b0

22

첫 번째 코드에서는 ssh에서 STDIN을“훔칠”것 while입니다. 피하기 위해 -n옵션을 추가 ssh하십시오. 보낸 사람 man ssh:

-n     Redirects stdin from /dev/null (actually, prevents reading from stdin).

좋아, 왜 이것이 for 루프에서 발생하지 않는지 알고 있습니까?
Kenny Rasschaert

7
for루프가 데이터를 입력이 아닌 매개 변수로 수신 하기 때문 입니다.
manatwork

1
여러분은 신사이자 학자입니다.
Kenny Rasschaert

0

기본 표준 입력 처리 ssh는 while 루프에서 나머지 라인을 비 웁니다.

이 문제를 피하려면 문제가있는 명령이 표준 입력을 읽는 위치를 변경하십시오. 표준 입력을 명령에 전달할 필요가 없으면 특수 /dev/null장치 에서 표준 입력을 읽으십시오 .

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