에코보다 printf가 더 좋은 이유는 무엇입니까?


548

나는 그것 printf보다 낫다고 들었습니다 echo. RHEL 5.8의 일부 프로그램에 텍스트를 공급하는 데는 효과 가 없었지만 사용 printf했기 때문에 사용해야했던 경험에서 인스턴스를 하나만 기억할 수 있습니다 . 그러나 분명히 다른 차이점이 있으며, 하나를 사용할 때와 다른 것을 사용할 때 특정 사례가 있는지뿐만 아니라 그 차이점을 묻고 싶습니다.echoprintf


에 대한 Whab echo -e?
neverMind9

1
@ neverMind9 echo -e는 (어떤 이유로 든) sh에서 작동하지 않으며 bash, 예를 들어 docker는 sh기본적으로 RUN명령에 사용 됩니다
Ciprian Tomoiagă

@ CiprianTomoiagă echo -e는 나를 위해 Android Terminal에서 작동합니다 sh.
neverMind9

답변:


757

기본적으로 이식성 (및 안정성) 문제입니다.

처음에는 echo옵션을 수락하지 않았으며 아무것도 확장하지 않았습니다. 공백 문자로 구분되고 개행 문자로 종료되는 인수를 출력하는 것이 전부였습니다.

이제 누군가 줄 echo "\n\t"바꿈 또는 탭 문자를 출력하거나 후행 줄 바꿈 문자를 출력하지 않는 옵션을 사용할 수 있다면 좋을 것이라고 생각했습니다 .

그런 다음 더 열심히 생각했지만 셸에 해당 기능을 추가하는 대신 ( perl큰 따옴표 안에 \t실제로 탭 문자가있는 것처럼)을 추가했습니다 echo.

데이빗 콘은 실수를 깨닫고 쉘 따옴표 새로운 형태의 도입 : $'...'나중에에 의해 복사 된 bash과를 zsh하지만 너무 늦은 시간에 의해했다.

표준 UNIX 지금 때 echo두 개의 문자가 포함 된 인수 수신 \하고 t대신 그들을 출력을, 그것은 탭 문자를 출력합니다. 그리고 \c인수에서 보자 마자 출력을 중지합니다 (따라서 후행 줄 바꿈도 출력되지 않습니다).

다른 쉘 / 유닉스 벤더 / 버전에서는 다르게 선택했습니다 : -e이스케이프 시퀀스를 확장하는 -n옵션 과 후행 줄 바꿈을 출력하지 않는 옵션을 추가했습니다 . 일부는 가지고 -E일부가, 이스케이프 시퀀스를 비활성화 할 -n수 있지만 -e, 하나에 의해 지원되는 이스케이프 시퀀스 목록 echo구현은 다른 지원으로 반드시 동일하지 않습니다.

Sven Mascheck에는 문제의 범위를 보여주는 멋진 페이지가 있습니다 .

이들에 echo옵션을 지원 구현하는 어떠한 지원 일반적으로 없다 --종료 옵션을 표시하는합니다 ( echo할 일부 비 본쉘의 내장, 그리고 zsh을 지원 -하는 생각에 대한) 예를 들어, 그래서 그것을 출력 어려운, "-n"echo에 많은 껍질.

같은 몇 가지 껍질에 bash¹ 또는 ksh93² 또는 yash( $ECHO_STYLE변수), 동작도 GNU 방법 쉘이 컴파일 또는 환경 (에 따라 echo'경우의 동작도 변경됩니다 $POSIXLY_CORRECT환경과 버전 함께 4 , zsh의 그것과 bsd_echo옵션, 일부 pdksh 기반 posix옵션 또는 호출 여부 sh). 따라서 bash echo동일한 버전의 두 개가 동일한 bash동작을 보장하지는 않습니다.

POSIX의 말 : 첫 번째 인수 -n이거나 임의의 인수에 백 슬래시가 포함되어 있으면 동작이 지정되지 않은 것 입니다. bash그 점에서 echo는 POSIX echo -e가 아닙니다. 예를 들어 -e<newline>POSIX가 요구 하는 대로 출력되지 않습니다 . UNIX 스펙은 더 엄격 -n하므로 \c출력을 중지하기 위해 이스케이프 시퀀스를 포함하여 일부 이스케이프 시퀀스의 확장을 금지 하고 필요로합니다 .

많은 구현이 호환되지 않는 경우 이러한 사양은 실제로 구출되지 않습니다. macOS 5 와 같은 일부 인증 된 시스템도 호환되지 않습니다.

현재 현실을 실제로 나타내려면 POSIX는 실제로 다음과 같이 말해야합니다 . 첫 번째 인수가 ^-([eEn]*|-help|-version)$확장 정규 표현식 과 일치 하거나 인수에 백 슬래시 (또는 αBIG5 문자 세트를 사용하는 로케일 과 같은 백 슬래시 문자 인코딩이 포함 된 문자)가 포함 된 경우 동작은 다음과 같습니다. 불특정.

대체로 백 슬래시 문자를 포함하지 않고로 시작하지 echo "$var"않는지 확인하지 않으면 어떤 결과 가 출력되는지 알 $var수 없습니다 -. POSIX 사양은 실제로 printf그 경우 대신 사용하도록 지시합니다 .

따라서 echo제어 할 수없는 데이터를 표시 하는 데 사용할 수 없습니다 . 즉, 스크립트를 작성 중이고 외부 입력 (사용자의 인수 또는 파일 시스템의 파일 이름 등)을 사용 echo하는 경우 이를 표시하는 데 사용할 수 없습니다 .

괜찮습니다 :

echo >&2 Invalid file.

이것은 아니다:

echo >&2 "Invalid file: $file"

( 컴파일 타임이나 환경을 통해 옵션이 활성화되지 않은 경우 echo와 같은 일부 (UNIX 호환이 아닌) 구현 에서는 정상적으로 작동하지만).bashxpg_echo

file=$(echo "$var" | tr ' ' _)대부분의 구현에서 확인되지 않은 경우 (예외적 인 yashECHO_STYLE=raw(주의 할 점으로 yash의 변수가 임의의 바이트 시퀀스 그렇게하지 임의의 파일 이름)를 길게 할 수 zshS ' echo -E - "$var"6 ).

printf반면,의 기본 사용법으로 제한되는 경우 더 안정적입니다 echo.

printf '%s\n' "$var"

$var포함 할 수있는 문자에 상관없이 개행 문자 뒤에 내용을 출력 합니다.

printf '%s' "$var"

후행 줄 바꿈 문자없이 출력합니다.

이제는 printf구현 간에 차이가 있습니다. POSIX에서 지정한 핵심 기능이 있지만 확장 기능이 많이 있습니다. 예를 들어, 일부 %q는 인수를 인용하기 위해 a 를 지원 하지만 어떻게 수행되는지는 쉘마다 다르며 일부 \uxxxx는 유니 코드 문자를 지원 합니다. 동작 printf '%10s\n' "$var"은 멀티 바이트 로케일에 따라 다르며 최소한 세 가지 다른 결과가 있습니다.printf %b '\123'

그러나 결국 POSIX 기능 세트를 고수 printf하고 너무 멋진 것을 시도하지 않으면 문제가 없습니다.

그러나 첫 번째 인수는 형식이므로 변수 / 제어되지 않은 데이터를 포함해서는 안됩니다.

다음을 echo사용하여 보다 안정적인 것을 구현할 수 있습니다 printf.

echo() ( # subshell for local scope for $IFS
  IFS=" " # needed for "$*"
  printf '%s\n' "$*"
)

echo_n() (
  IFS=" "
  printf %s "$*"
)

echo_e() (
  IFS=" "
  printf '%b\n' "$*"
)

local IFS많은 쉘을 사용하거나 다음과 같이 작성하면 서브 쉘 (대부분의 쉘 구현에서 추가 프로세스를 생성 함을 의미 함)을 피할 수 있습니다 .

echo() {
  if [ "$#" -gt 0 ]; then
     printf %s "$1"
     shift
  fi
  if [ "$#" -gt 0 ]; then
     printf ' %s' "$@"
  fi
  printf '\n'
}

노트

1. 방법 bashecho변경 될 수 있습니다 행동.

으로 bash, 실행시의 동작을 제어하는 두 가지가 있습니다 echo(옆 enable -n echo또는 재정의 echo기능 또는 별칭으로)를 다음 xpg_echo bash옵션을 여부는 bashPOSIX 모드에 있습니다. posix모드 bash가로 호출 sh되거나 POSIXLY_CORRECT환경에 있거나 posix옵션을 사용하는 경우 활성화 할 수 있습니다 .

대부분의 시스템에서 기본 동작 :

$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character

xpg_echo UNIX가 요구하는 순서를 확장합니다 :

$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A

여전히 명예 -n-e(그리고 -E) :

$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%

xpg_echoPOSIX 모드 :

$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A

이번에 bash는 POSIX와 UNIX를 모두 준수합니다. POSIX 모드에서는 다음 bash과 같이 출력되지 않으므로 여전히 POSIX 호환이 아닙니다 -e.

$ env SHELLOPTS=posix bash -c 'echo -e'

$

xpg_echo 및 posix의 기본값은 컴파일시 --enable-xpg-echo-default--enable-strict-posix-default옵션을 configure스크립트로 사용하여 정의 할 수 있습니다 . 그것은 일반적으로 최신 버전의 OS / X가 빌드하기 위해 수행하는 작업 /bin/sh입니다. 올바른 마음으로 유닉스 / 리눅스 구현 / 배포는 일반적으로 그렇게하지 /bin/bash않습니다 . 사실, 사실은 아닙니다. /bin/bashOracle이 Solaris 11 (선택적 패키지)과 함께 제공 --enable-xpg-echo-default되는 것은 Solaris 10의 경우와 달리 내장 된 것 같습니다 .

2. 방법 ksh93echo동작을 변경할 수 있습니다.

에서 이스케이프 시퀀스를 확장하고 옵션을 인식 ksh93하는지 여부는 환경 변수 및 / 또는 환경 변수 echo의 내용에 따라 다릅니다 .$PATH$_AST_FEATURES

경우는 $PATH포함 된 구성 요소 포함 /5bin또는 /xpg전과 /bin또는 /usr/bin다음은 시스템 V / UNIX의 방식으로 작동 구성 요소 (시퀀스를 확장, 옵션을 적용하지 않습니다). 찾 /ucb거나 /bsd먼저 또는 $_AST_FEATURES7에 포함되어 있으면 UNIVERSE = ucbBSD 3 방식으로 작동합니다 ( -e확장 가능, 인식 -n).

기본값은 시스템에 따라 다릅니다 (데비안의 BSD) ( builtin getconf; getconf UNIVERSE최신 버전 ksh93 의 출력 참조 ).

$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD

3. echo -e를위한 BSD?

-e옵션 처리를위한 BSD에 대한 언급 은 약간 오해의 소지가 있습니다. 서로 다르고 호환되지 않는 echo행동의 대부분은 AT & T에서 소개되었습니다.

  • \n, \0ooo, \c프로그래머의 워크 벤치 (유닉스 V6 기준) UNIX, 나머지 (에서 \b, \r유닉스 시스템 III에서 ...) 참조 .
  • -n유닉스 V7에서 (Dennis Ritchie Ref가 작성 )
  • -e유닉스 V8에서 (Dennis Ritchie Ref가 작성 )
  • -E자체는 아마도 처음 온 bash(에 CWRU / CWRU.chlog 1.13.5 버전 , 브라이언 폭스 1992년 10월 18일에 추가 언급 GNU의 echo십일 이후 출시 된 SH-utils를 1.8에서 조금 후에 복사를)

그동안 echo의 내장 sh또는 BSD의 지원했다 -e가 90 년대 초에 대한 Almquist 쉘을 사용하기 시작한 날 이후, 독립 echo이 날 유틸리티 (거기를 지원하지 않습니다 FreeBSD는echo 아직 지원하지 않습니다 -e는 지원을하지 불구하고, -n같은 유닉스 V7 (그리고 \c마지막 인수의 끝에서만).

의 처리 -e에 추가 ksh93echoBSD의에 때 우주 와 2006 년에 발표 된 ksh93r 버전에서 컴파일시에 해제 할 수 있습니다.

8.31의 GNU 에코 동작 변경

로 coreutils 8.31 (이후 이 커밋 ), GNU는 echo지금 POSIXLY_CORRECT 환경에있을 때의 동작과 일치하도록 기본적으로 이스케이프 시퀀스 확장 bash -o posix -O xpg_echo의의 echo(참조 내장을 버그 리포트를 ).

5. macOS echo

대부분의 macOS 버전은 OpenGroup으로부터 UNIX 인증을 받았습니다 .

그들의 sh내장은 echo그것의로 준수 bash와 내장 (아주 오래된 버전) xpg_echo기본적으로 사용할 수는 있지만, 자신의 독립 실행 형 echo유틸리티가 없습니다. env echo -n대신에 출력하는 것도 -n<newline>, env echo '\n'출력하지 \n<newline>대신 <newline><newline>.

이것은 /bin/echoFreeBSD의 첫 번째 인수 인 경우 첫 번째 인수 인 경우 줄 바꿈 출력을 억제 -n하고 마지막 인수가 끝나는 경우 (1995 년 이후) 줄 바꿈 출력을 억제 \c하지만 UNIX에서 필요로하는 다른 백 슬래시 시퀀스는 지원하지 않습니다 \\.

6. echo임의의 데이터를 그대로 출력 할 수있는 구현

엄밀히 말하면, 당신은 또한 FreeBSD의 / 맥 OS 것을 셀 수 말하기 /bin/echo(안 자신의 쉘 위의 echo내장은) 여기서 zshecho -E - "$var"또는 yash의가 ECHO_STYLE=raw echo "$var"( printf '%s\n' "$var") 작성할 수 있습니다 :

/bin/echo "$var
\c"

지원 구현 -E-n(또는로 구성 할 수 있습니다)도 수행 할 수 있습니다

echo -nE "$var
"

그리고 zsh's이 (가) echo -nE - "$var"( printf %s "$var") 기록 될 수있다

/bin/echo "$var\c"

7. _AST_FEATURES그리고 ASTUNIVERSE

_AST_FEATURES직접 조작하는 것은 아닙니다, 명령 실행을 통해 AST 구성 설정을 전파하는 데 사용됩니다. 구성은 문서화되지 않은 astgetconf()API 를 통해 수행되어야합니다 . 내부 ksh93에서 getconf내장 (을 사용 builtin getconf하거나 호출 하여 사용 가능 command /opt/ast/bin/getconf)은astgetconf()

예를 들어 설정을 ( SysV 방식으로 작동 builtin getconf; getconf UNIVERSE = att하도록) 변경해야 UNIVERSE합니다 . 그 후에 환경 변수에 포함되어 있음을 알 수 있습니다 .attecho$_AST_FEATURESUNIVERSE = att


13
초기 유닉스 개발이 많이 이루어 졌으며 '인터페이스를 변경할 때 이름 변경' 과 같은 훌륭한 소프트웨어 엔지니어링 원칙 이 적용되지 않았습니다.
Henk Langeveld

7
참고 로 인용 구문의 일부로 쉘과 반대로 시퀀스 를 echo확장하면 얻을 수있는 장점은 \xNUL 바이트를 출력 할 수 있다는 것입니다 (유닉스의 또 다른 잘못된 디자인은 null로 구분됩니다) (같은 반 시스템 호출 문자열 execve())) 바이트의 임의의 시퀀스를받을 수 없어
스테판 Chazelas가

Sven Maschek의 웹 페이지에서 볼 수 있듯이 printf대부분의 구현에서 잘못했기 때문에 문제가되는 것 같습니다. 내가 아는 유일한 올바른 구현 bosh은 Sven Maschek의 페이지에를 통해 null 바이트 문제를 나열하지 않습니다 \0.
schily

1
@ StéphaneChazelas를 작성해 주셔서 감사합니다.
JoshuaRLi

28

printf형식 옵션 에 사용할 수 있습니다 . echo변수 또는 (간단한) 줄의 값을 인쇄 할 때 유용하지만 그게 전부입니다. printf기본적으로 C 버전의 기능을 수행 할 수 있습니다.

사용법 및 기능 예 :

Echo:

echo "*** Backup shell script ***"
echo
echo "Runtime: $(date) @ $(hostname)"
echo

printf:

vech="bike"
printf "%s\n" "$vech"

출처 :


@ 0xC0000022L 감사합니다. 질문에 답하기 위해 서두르면서 잘못된 사이트에 연결된 것을 보지 못했습니다. 당신의 기여와 수정에 감사드립니다.
NlightNFotis

7
사용 echo변수의 값이 메타 문자가 포함 된 경우 변수를 인쇄하는 것은 실패 할 수 있습니다.
Keith Thompson

17

한 가지 "이점"은 호출하려는 경우와 같은 echo특정 이스케이프 시퀀스를 해석 하는 것처럼 말할 필요가 없다는 것 \n입니다. 그것들을 해석하는 것을 알고 있으며 -e그렇게 할 필요가 없습니다 .

printf "some\nmulti-lined\ntext\n"

(NB : 마지막 옵션 \n이 필요 echo합니다. -n옵션 을 제공하지 않으면 암시합니다 )

echo -e "some\nmulti-lined\ntext"

의 마지막 부분 \n에 유의하십시오 printf. 하루가 끝나면 맛과 요구 사항이 중요합니다. echo또는 printf.


1
트루 /usr/bin/echobash내장. dash, ksh그리고 zsh내장은 echo필요하지 않습니다 -e백 슬래시 이스케이프 문자를 확장 스위치를.
manatwork

겁이 나는 이유는 무엇입니까? 당신의 말은 그것이 반드시 진정한 이점은 아니라는 것을 암시합니다.
Keith Thompson

2
@KeithThompson는 : 실제로는 모든 의미 암시는 모든 사람이 그것을 장점 고려할 수 있다는 것이다.
0xC0000022L

그것에 대해 자세하게 말 해 주실 수 있나요? 왜 유리하지 않습니까? "당신이 그것을 부르고 싶다면"이라는 문구는 그렇지 않다고 생각한다는 것을 강력하게 암시합니다.
Keith Thompson

2
아니면 그냥 할 수 있습니다 printf '%s\n' 'foo\bar.
nyuszika7h

6

printf내장 쉘 echo이 훨씬 빠르기 때문에 성능의 단점이 있습니다. 이는 특히 새 명령의 각 인스턴스가 Windows 오버 헤드를 많이 발생시키는 Cygwin에서 작동합니다. 에코가 많은 프로그램을 /bin/echo쉘의 에코로 사용하지 않도록 변경 하면 성능이 거의 두 배가되었습니다. 이식성과 성능 사이의 균형입니다. 항상을 사용하는 것은 슬램 덩크가 아닙니다 printf.


15
printf오늘날 대부분의 쉘에 내장되어 있습니다 (bash, dash, ksh, zsh, yash, 일부 pdksh 파생 상품 ... 일반적으로 cygwin에서도 발견되는 쉘도 포함). 유일하게 눈에 띄는 예외는 일부 pdksh파생 상품입니다.
Stéphane Chazelas

그러나 이러한 printf 구현 중 많은 부분이 깨졌습니다. 이것은 널 printf바이트를 출력하는 데 사용 하고자 할 때 필수적 이지만 일부 구현에서는`\ c '를 형식 문자열로 해석하지 않아야하지만 해석합니다.
schily

2
@schily, 형식 문자열에서 printf사용하는 경우 POSIX 에서 동작을 지정 하지 \c않으므로 printf구현은 원하는 방식으로 구현할 수 있습니다. 예를 들어, 일부는 PWB에서와 동일하게 취급하고 echo(printf가 종료되기 때문에), ksh는 \cA제어 문자 (printf 형식 인수의 경우와 nor가 $'...'아닌 )에이를 사용합니다 . 그 NUL 바이트를 인쇄 함께 할 수있다, 또는 어쩌면 당신이 다스 려하는지 확실하지 의 ? echoprintkshprintf '\c@'
Stéphane Chazelas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.