Bash를 사용하여 파일에서 줄 읽기: for 및 while

Bash를 사용하여 파일에서 줄 읽기: for 및 while

bash 스크립트를 사용하여 텍스트 파일을 읽고 각 줄에서 특정 작업을 수행하려고 합니다.

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

server1
server2
server3
server4

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

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

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

그러나 cat을 사용하는 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

첫 번째 반복 후 첫 번째 예제가 중지되는 이유는 무엇입니까?

답변1

이곳의 순환은 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에서 무엇이 잘못되었는지 살펴보겠습니다. 서버 목록 파일의 리디렉션은 루프 전체에 적용됩니다. 따라서 run 을 실행하면 ssh표준 입력이 이 파일에서 나옵니다. SSH 클라이언트는 원격 애플리케이션이 언제 표준 입력에서 데이터를 읽으려고 하는지 알 수 없습니다. 따라서 SSH 클라이언트가 일부 입력을 발견하면 해당 입력을 원격 측으로 보냅니다. 그런 다음 SSH 서버는 필요한 경우 해당 입력을 원격 명령에 제공할 수 있습니다. 귀하의 경우 원격 명령은 입력을 전혀 읽지 않으므로 데이터는 폐기되지만 클라이언트는 이에 대해 아무것도 모릅니다. 귀하의 시도는 입력이 읽혀지지 않고 표준 입력만 유지하기 echo때문에 작동합니다 .echo

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

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에서 파일을 열고 read파일 설명자 3에서 명령의 표준 입력을 리디렉션합니다. SSH 클라이언트는 공개된 비표준 설명자를 무시하므로 모든 것이 잘 작동합니다.

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

Bash에서 read명령에는 다른 파일 설명자에서 읽을 수 있는 특정 옵션이 있으므로 read -u3 server.

관련 독서:파일 설명자 및 쉘 스크립트;추가 파일 설명자는 언제 사용됩니까?

답변2

당신은 사용해야합니다while, 아니요for. 그러한 루프에서 표준 입력을 삼키는 명령을 피하는 방법은 단순히 다른 명령을 사용하는 것입니다.파일 설명자:

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

더 많은 정보를 알고 싶으시면, help [r]ead(진짜) 그리고다른 기사에서는 그 이유를 설명합니다..

답변3

이를 방지 하려면 첫 번째 코드에서 ssh옵션 을 추가하세요 while. 에서 :-nsshman ssh

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

답변4

기본 표준 입력 처리는 sshwhile 루프의 나머지 라인을 모두 소모합니다.

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

관련 정보