“while read…”를 사용하면, echo와 printf는 다른 결과를 얻습니다 ” echo ‘1 2

이 질문에 따르면 “리눅스 스크립트에서”while read … “사용하기

echo '1 2 3 4 5 6' | while read a b c;do echo "$a, $b, $c"; done

결과:

1, 2, 3 4 5 6

하지만 내가 교체 echo하면printf

echo '1 2 3 4 5 6' | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done

결과

1, 2, 3
4, 5, 6

누군가이 두 명령이 어떻게 다른지 말해 줄 수 있습니까? 고마워 ~



답변

단순히 에코 대 printf가 아닙니다.

먼저, read a b c부분 이 어떻게되는지 이해합시다 . space-tab-newline readIFSvariable 의 기본값을 기반으로 단어 분할을 수행 하고 그에 따라 모든 것을 맞출 것입니다. 보유 할 변수보다 더 많은 입력이 있으면 분할 된 부분을 첫 번째 변수에 맞추고 적합하지 않은 것은 마지막에갑니다. 여기 내가 의미하는 바가있다 :

bash-4.3$ read a b c <<< "one two three four"
bash-4.3$ echo $a
one
bash-4.3$ echo $b
two
bash-4.3$ echo $c
three four

이것은 bash설명서에 설명 된 것과 정확히 같습니다 (응답 끝의 인용문 참조).

귀하의 경우 1과 2는 a와 b 변수에 적합하고 c는 다른 모든 것을 취 3 4 5 6합니다.

또한 여러 번 보게 될 것은 사람들이 while IFS= read -r line; do ... ; done < input.txt텍스트 파일을 한 줄씩 읽는 데 사용한다는 것 입니다. 다시 말하지만, IFS=단어 분할을 제어하거나 더 구체적으로-단어를 ​​비활성화하고 한 줄의 텍스트를 변수로 읽는 이유입니다. 존재하지 않는다면 read각 개별 단어를 line변수 에 맞추려고 합니다. 그러나 그것은 또 다른 이야기 while IFS= read -r variable입니다. 매우 자주 사용되는 구조 이기 때문에 나중에 공부하도록 권장합니다 .

에코 대 printf 동작

echo당신이 여기서 기대하는 것을 수행합니다. 변수 read를 정렬 한 그대로 표시합니다 . 이것은 이미 이전 토론에서 입증되었습니다.

printf변수가 모두 소진 될 때까지 형식 문자열에 변수를 계속 맞추기 때문에 매우 특별합니다. 따라서 printf "%d, %d, %d \n" $a $b $cprintf를 사용 하면 소수점 이하 3 자의 형식 문자열이 표시되지만 3보다 많은 인수가 있습니다 (변수가 실제로 개별 1,2,3,4,5,6으로 확장되기 때문입니다). 혼동되는 것처럼 들릴 수 있지만 C 언어에서 실제 printf() 기능이 수행 하는 동작의 개선 된 동작으로 인해 존재 합니다.

출력에 영향을 미치는 여기서 수행 한 작업은 변수가 따옴표로 묶여 있지 않으므로 쉘 ( printf)이 변수를 6 개의 개별 항목으로 나눌 수 있습니다. 이것을 따옴표와 비교하십시오.

bash-4.3$ read a b c <<< "1 2 3 4"
bash-4.3$ printf "%d %d %d\n" "$a" "$b" "$c"
bash: printf: 3 4: invalid number
1 2 3

정확히 $c변수가 따옴표로 묶여 있기 때문에 이제는 하나의 전체 문자열로 인식되며 형식에 3 4맞지 않습니다 ( %d단일 정수).

이제 인용하지 않고 똑같이하십시오 :

bash-4.3$ printf "%d %d %d\n" $a $b $c
1 2 3
4 0 0

printf 다시 말하길 : “여러분은 6 개의 아이템을 가지고 있지만 포맷은 단지 3을 보여 주므로, 사용자의 실제 입력에 맞지 않는 것을 맞추고 비워 둘 것입니다”.

그리고이 모든 경우에 당신은 그것에 대해 내 말을 할 필요가 없습니다. 그냥 실행 strace -e trace=execve하고 명령이 실제로 “을 참조하십시오”무엇을 직접 볼 :

bash-4.3$ strace -e trace=execve printf "%d %d %d\n" $a $b $c
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3", "4"], [/* 80 vars */]) = 0
1 2 3
4 0 0
+++ exited with 0 +++

bash-4.3$ strace -e trace=execve printf "%d %d %d\n" "$a" "$b" "$c"
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3 4"], [/* 80 vars */]) = 0
1 2 printf: 3 4’: value not completely converted
3
+++ exited with 1 +++

추가 사항

Charles Duffy가 주석에서 올바르게 지적했듯이 명령에 사용하는 bash자체 내장 기능이 있으며 실제로는 쉘 버전이 아닌 버전을 호출 합니다. 사소한 차이점을 제외하고이 특정 질문에 대한 우리의 관심을 끌기 위해 표준 형식 지정자는 동일하고 동작은 동일합니다.printfstrace/usr/bin/printf

명심해야 할 것은 printf구문이 echoC 또는 C와 유사한 언어에 익숙하다는 점은 말할 것도없이 구문이 이식성이 뛰어 나기 때문에 선호 된다는 printf()것입니다. vs 의 주제에 대해 terdon훌륭한 답변을 참조하십시오 . 특정 버전의 Ubuntu에서 특정 셸에 맞게 출력을 조정할 수 있지만 다른 시스템에 스크립트를 이식하려는 경우 echo보다는 선호하는 것이 좋습니다. Ubuntu 및 CentOS 시스템을 사용하는 초보자 시스템 관리자이거나 FreeBSD (아는 사람도 있음) 일 수도 있으므로 그러한 경우에는 선택해야합니다.printfechoprintf

bash 매뉴얼, SHELL BUILTIN COMMANDS 섹션에서 인용

[-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name … ]

한 줄은 표준 입력 또는 -u 옵션에 대한 인수로 제공된 파일 디스크립터 fd에서 읽히고 첫 번째 단어는 첫 번째 이름에 지정되고 두 번째 단어는 두 번째 이름에 지정됩니다. 성에 지정된 단어와 그 중간 분리 자. 입력 스트림에서 읽은 단어가 이름보다 적은 경우 나머지 이름에는 빈 값이 할당됩니다. IFS의 문자는 쉘이 확장에 사용하는 것과 동일한 규칙을 사용하여 행을 단어로 분할하는 데 사용됩니다 (위의 단어 분할에서 설명).


답변

이것은 제안 일 뿐이며 Sergiy의 답변을 전혀 대체하지는 않습니다. Sergiy가 왜 인쇄 방식이 다른지에 대해 큰 답변을했다고 생각합니다. 읽기에 대한 변수는로 남아있는 할당되는 방식 $c으로 변수 3 4 5 612에 할당 a하고 b. echo여기서 당신을 위해 변수를 분할하지 않습니다 printf%d의.

그러나 명령의 시작 부분에서 숫자의 에코를 조작하여 기본적으로 동일한 대답을 제공 할 수 있습니다.

에서 /bin/bash사용할 수 :

echo -e "1 2 3 \n4 5 6"

에서 /bin/sh그냥 사용할 수 있습니다 :

echo "1 2 3 \n4 5 6"

Bash는 -e를 사용하여 sh가 이미 활성화되어 있으므로 sh가 필요하지 않은 \ 이스케이프 문자를 활성화합니다. \n새 줄을 만들므로 이제 에코 줄이 두 개의 별도 줄로 나뉘어 에코 루프 문에 두 번 사용할 수 있습니다.

:~$ echo -e "1 2 3 \n4 5 6" | while read a b c; do echo "$a, $b, $c"; done
1, 2, 3
4, 5, 6

다음 printf명령 을 사용하여 동일한 출력을 생성합니다 .

:~$ echo -e "1 2 3 \n4 5 6" | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done
1, 2, 3
4, 5, 6 

sh

$ echo "1 2 3 \n4 5 6" | while read a b c; do echo "$a, $b, $c"; done
1, 2, 3
4, 5, 6
$ echo "1 2 3 \n4 5 6" | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done
1, 2, 3
4, 5, 6 

도움이 되었기를 바랍니다!


답변