"while read…"를 사용하면, echo와 printf는 다른 결과를 얻습니다


14

이 질문에 따르면 "리눅스 스크립트에서"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

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

답변:


24

단순히 에코 대 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의 문자는 쉘이 확장에 사용하는 것과 동일한 규칙을 사용하여 행을 단어로 분할하는 데 사용됩니다 (위의 단어 분할에서 설명).


2
strace대소 문자와 대소 문자 사이의 주목할만한 차이점 중 하나 strace printf는을 사용 /usr/bin/printf하는 반면 printfbash에서 직접 동일한 이름으로 내장 된 쉘을 사용하는 것입니다. 예를 들어, bash 인스턴스에는 형식 지정자 %q와 새 버전에서 $()T시간 형식 지정이 항상 같은 것은 아닙니다 .
찰스 더피

7

이것은 제안 일 뿐이며 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 

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


echo -ePOSIX의 확장 일뿐만 아니라 입력에서 실제로 출력 echo되지 않는 -e입력은 실제로 블랙 레터 사양을 무시 / 파괴합니다. bash는 기본적으로 비 호환이지만 둘 다 posixxpg_echo플래그가 설정된 경우 표준을 준수합니다 . 후자 컴파일시 기본값으로 설정 될 있습니다. 즉, echo -e여기에서 예상 한대로 모든 버전의 bash가 작동하지는 않습니다 .
찰스 더피

POSIX 대한 사양의 애플리케이션 사용하고 RATIONALE 섹션을 참조 echopubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html , 명시 적으로 기술 echo의 문제가 하나의 존재 인 불특정 -n어딘가에 입력 또는 백 슬래시, 그 상담 printf대신 사용하십시오.
찰스 더피

@CharlesDuffy 필자가 모든 버전의 bash에서 작동한다고 생각한 적이 있습니까? 그리고 내 대답에 어떤 문제가 있습니까? 더 나은 해결책이 있다고 생각되면 사람들을 잘못 증명하는 대신 답으로 쓰십시오. 나는 당신과 같은 사람들 과이 방법을 너무 여러 번 겪었 으므로이 의견에 그만 두십시오. 그게 전부 야
Terrance

3
Ask Ubuntu의 @CharlesDuffy는 때때로 다른 Linux 배포판으로 전송할 수없는 답변을 작성합니다. 귀하의 담당자는 103 점이므로 눈치 채지 못할 수도 있습니다. 스택 오버플로에 대한 귀하의 담당자는 123.3K 포인트이므로 일반적으로 * NIX 시스템에 대해 많은 것을 알 수 있습니다. 나는 당신, 테라스와 Serg가 괜찮다고 생각하지만 다른 각도에서 실을보고 있습니다. 나는 당신이 * NIX 포터블이거나 적어도 리눅스 배포판에서 포터블 한 답변을 작성하는 것에 대한 정서를 반향을 불러 일으킨다.
WinEunuuchs2Unix

2
@CharlesDuffy 귀하의 감정에 대한 답변 휴대용으로 작성 해야 한다는 데 동의하지만 , 결국 우분투 중심 사이트이므로 사용자는 우분투 전용 도구를 사용해야합니다. 따라서 경고는 다소 암시됩니다. 지나치게 긴 토론에 들어 가지 마십시오. 당신은 다른 포탄을 고려해야하고, 대답으로부터 배우기 위해 읽는 초보자도 고려해야합니다. 짧은 의견이 충분할 것입니다.
Sergiy Kolodyazhnyy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.