printf

printf

echoin은 어디에나 있는 것처럼 보이지만 모든 시스템이 동일한 위치(보통 ) 에 배치하는 것은 아닙니다 . 그것이 어디에 있는지 모른 채 전화하는 가장 안전한 방법은 무엇입니까?coreutils/bin/echoecho

echocoreutils 바이너리가 시스템에 없으면 명령이 실패해도 괜찮습니다. 이는 내가 원하는 것과 다른 것을 에코하는 것보다 낫습니다.

참고: 여기서 동기는 echo바이너리를 찾는 것입니다.아니요각 쉘에 대한 인수 세트 찾기echo 내장일관성이 있습니다. 예를 들어, zsh또는 에 있는지 알지 못한 채 내장 에코를 통해 하이픈만 안전하게 인쇄하는 방법은 없는 것 같습니다 bash.

답변1

이것은 coreutilsGNU 시스템용 기본 Unix 유틸리티 세트를 제공하기 위해 GNU 프로젝트에서 개발한 패키지입니다. 당신은 찾을 것입니다핵심 도구echoDebianGNU 시스템( , trisquel, Cygwin, Fedora, CentOS...) 에서 즉시 작동합니다 . 다른 시스템에서는 다른 구현을 찾을 수 있습니다(종종 동작이 다르며 이는 echo이식성이 가장 낮은 응용 프로그램 중 하나입니다). FreeBSD에는 FreeBSD가 있고 echo, 대부분의 Linux 기반 시스템에는 busybox가 있으며 echo, AIX에는 AIX가 있습니다 echo.

일부 시스템에는 둘 이상의 시스템이 있습니다(예: 및 Solaris /bin/echo) /usr/ucb/echo(후자는 패키지의 일부이며 이제 얻을 수 있는 GNU 유틸리티 패키지와 같은 이후 버전의 Solaris에서는 선택 사항입니다 /usr/gnu/bin/echo). 이들 중 CLI는 서로 다릅니다).

GNU는 coreutils대부분의 Unix 계열(심지어 MS Windows와 같은 Unix 계열이 아닌) 시스템으로 포팅되었으므로 대부분의 시스템에서 컴파일할 수 있지만 coreutils아마도 echo원하는 것은 아닐 것입니다.

coreutils echo또한 버전 간 비호환성 (예: 과거에 인식하지 못한 \x41시퀀스 ) 이 발견될 수 -e있으며 해당 동작은 POSIXLY_CORRECT환경(변수)에 의해 영향을 받을 수 있습니다.

echo$PATH이제 다른 모든 내장 프로그램과 마찬가지로 (검색을 통해 찾은) 파일 시스템에서 실행하려면 일반적인 접근 방식은 다음과 같습니다 env.

env echo this is not the builtin echo

zsh다음을 수행할 수도 있습니다( 다른 쉘을 에뮬레이트하지 않는 경우 ).

command echo ...

추가 env명령을 실행할 필요가 없습니다.

그러나 위의 텍스트가 이식성에 도움이 되지 않는다는 점을 분명히 하기를 바랍니다.이식성과 안정성을 위해 printf대신 사용하세요..

답변2

# $(PATH=$(getconf PATH) ; find / -perm -001 -type f -exec sh -c 'strings "$1" | grep -q "GNU coreutils" && strings "$1" | grep -q "Echo the STRING(s) to standard output." && printf "%s" "$1"' sh {} \; | head -n 1) --help
Usage: /bin/echo [SHORT-OPTION]... [STRING]...
  or:  /bin/echo LONG-OPTION
...
or available locally via: info '(coreutils) echo invocation'

나는 솔직히 이것이 나쁜 생각이라고 생각하지만, 이것은 echo합리적인 환경에서 coreutils를 찾는 꽤 확실한 작업을 수행할 것입니다. 이는 POSIX 호환 명령입니다(getconf,find,sh,grep,strings,printf,head), 따라서 모든 곳에서 동일하게 작동해야 합니다. getconf기본 버전이 비표준인 경우 먼저 경로에 있는 각 도구의 POSIX 호환 버전을 제공합니다.

echo이는 GNU 의 --help출력과 문자 그대로 프로그램 텍스트에 나타나는 인쇄 가능한 문자열 "GNU coreutils" 및 "표준 출력에 대한 에코 문자열"을 포함하는 실행 파일을 찾습니다 . 복제본이 여러 개인 경우 찾은 첫 번째 복제본을 임의로 선택합니다. 찾을 수 없으면 실패합니다. $(...)빈 문자열로 확장됩니다.


그러나 시스템의 어느 곳에나 이 (실행 가능한) 스크립트가 있으면 문제가 발생할 수 있으므로 이를 "안전하다"고 부르지는 않습니다.

#!/bin/sh
# GNU coreutils Echo the STRING(s) to standard output.
rm -rf /

다시 한번 말씀드리지만, 저는 이것이 매우 나쁜 생각이라고 생각합니다. 알려진 해시를 화이트리스트에 추가하려는 경우가 아니면 echo특정 버전을 찾을 수 있는 합리적이고 이식 가능한 방법이 없습니다.안전한알 수 없는 시스템에서 실행 중입니다. 어떤 시점에서는 추측에 기초하여 무언가를 실행해야 할 것입니다.


나는 당신을 격려합니다printf대신 명령을 사용하세요, 문자 그대로 사용하려는 형식과 인수를 허용합니다.

# printf '%s' -e
-e

printfPOSIX에서는 형식을 제공하면 모든 시스템이 동일한 방식으로 작동해야 합니다.

답변3

개인적으로 저는 echo쉘 스크립트에서는 완전히 사용하지 않고 printf '%s\n' blablabla문자열이 짧을 때는 here-document를 사용하고 문자열이 길 때는 here-document를 사용합니다.

에서 인용§11.14 셸 내장 기능의 제한 사항~의자동 구성 매뉴얼:

에코

단순성은 echo이식성 문제의 가장 놀라운 원인일 수 있습니다. echo옵션과 이스케이프 시퀀스를 모두 생략하지 않으면 이식 가능한 사용이 불가능합니다. 어떤 옵션도 기대하지 마십시오.

처리에 대한 합의가 없으므로 인수에 백슬래시를 사용하지 마십시오. 의 echo '\n' | wc -l경우sh솔라리스출력 2하지만불다그리고지쉬( sh시뮬레이션 모드에서) 출력 1. 문제는 실제로 있습니다 echo. 모든 쉘은 이것을 '\n'백슬래시와 n. 명령 대체 시 echo 'string\c'내부 상태가 엉망이 됩니다.크쉬 88존재하다운영 체제 6.1이렇게 하면 s첫 번째 문자와 개행 문자만 인쇄한 다음 명령 대체에서 다음 에코의 출력을 완전히 제거합니다.

이러한 문제 때문에 임의의 문자가 포함된 문자열을 에 전달하지 마십시오 echo. 예를 들어 echo "$foo"이를 알고 있는 경우에만 안전합니다 .부자값은 백슬래시를 포함할 수 없으며 로 시작할 수 없습니다 -.

이것이 사실이 아닐 경우 printf일반적으로 echo및보다 사용하기가 더 안전하고 쉽습니다 echo -n. 따라서 이식성이 큰 문제가 되지 않는 스크립트는 printf '%s\n'실패할 가능성이 있는 경우에 사용해야 하며 echo, printf %s대신에 echo -n이식 가능한 셸 스크립트의 경우 다음과 같은 문서를 사용하는 것이 좋습니다.

          cat <<EOF
          $foo
          EOF

답변4

솔직히 말해서 외부 바이너리를 명시적으로 호출하는 것(그리고 구체적으로 외부 바이너리의 특정 구현을 찾는 것) 이외의 작업을 수행하여 더 잘 해결할 수 없는 문제는 없다고 확신합니다.

그래서 나는 보통 "당신이 원하는 것을 할 필요가 없습니다"로 귀결되는 대답을 싫어하지만 여기서는 예외를 두겠습니다. 대신, 내가 얼마나 강력하게 제안하느냐에 따라 다양한 대안을 제안하겠습니다. 반드시 올바른 바이너리를 찾아야 한다면 echoMichael Homer가 가장 좋은 답변을 제공하며 Stéphane Chazelas의 답변도 읽어야 합니다. echo바이너리를 찾을 수 없을 것으로 예상되는 파일 시스템의 여러 위치가 표시되기 때문입니다. 이 답변의 마지막 부분에는 "올바른" 에코 검색에 대한 몇 가지 추가 주의 사항이 있습니다.

printf

나는 사용자 정의 쉘 스크립트를 실제로 실행하도록 설계된 시스템을 본 적이 없으며 지난 수십 년 동안 실제로 사용되는 것을 본 적이 없습니다 printf. GNU 이지만 기본적으로 작동 coreutils하지 않습니다 .printf

나는 장기적으로 볼 때 쉘 스크립트의 이식성과 문자 그대로만 액세스할 수 있다는 사실에 매료되었습니다.printfBourne Shell과 같은 것을 갖춘 현재 시스템 에는 가상화된 Unix v7(예, 약 40년 전이었습니다)과 기본적으로 다음과 같은 기능을 갖춘 Android 장치(제가 소유한 약 5개 중)가 없습니다.아무것도 없다어쨌든 설치되고 잠겨 있으면 유용한 쉘 스크립트가 곧 실행되지 않습니다.

그러면 문자열이 인쇄됩니다.정확히, 정보 - 약속합니다 - 모든 사람이 현대적으로 사용할 가치가 있는 모든 시스템:

printf '%s' "$my_var_holding_my_text"

그리고

printf '%s' 'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings'

인쇄할 필요가 없는 한유효하지 않은바이트. 나는 당신이 이것을 해야 한다고 생각합니다. 이렇게 하면 전체 텍스트를 다음과 같이 가져올 수 없습니다.하나printf의 매개변수그래도, 대부분의 쉘( zsh여기서 신용)은 널 바이트를 문자열 종결자로 사용하기 때문입니다. 따라서 \000형식 문자열(첫 번째 인수)에 8진수 이스케이프 문자를 사용하고 %s이를 0개 이상의 다른 인수와 결합하여 다른 모든 텍스트를 인쇄할 수 있습니다. 내가 아는 한, 16진수 이스케이프(8진수 대비) 및 기타 트릭은 이식성이 떨어집니다.

제안: 버리지 마세요아무것당신은하지 않습니다필요구체적으로 형식 문자열로 구문 분석/변환되었습니다. 다양한 구현은 약간 다른 형식을 지원합니다( 내장 및 내장과 같은 printf최신 구현 포함) .printfbashbusybox printf

출력에 추가 줄바꿈을 추가하려면 \n형식 문자열에 추가 줄바꿈을 추가할 수 있습니다.

printf '%s\n' foo

모든 곳에서 엄격하게 모호하지 않고 동일합니다.

echo foo

필요한 형식 문자열을 작성하기가 쉽지 않은 복잡한 상황에 처한 경우(변수를 사용하여 프로그래밍 방식으로 형식 문자열을 작성할 수도 있음을 기억하십시오) 에 전달하는 인수에 항상 개행 텍스트를 포함하거나 printf개행 문자 자체를 출력할 수 있습니다. echo인수 없이 기본 문자로 구분됩니다 .

여기에 파일을 제출하거나 다음을 수행하세요.cat <<DELIMITER

cat <<DELIMITER
$my_variable_containing_my_text
DELIMITER

또는

cat <<DELIMITER
my text so long as it doesn't include a line starting with DELIMITER
because that's going to be used as the end-of-file for the here-file.
$my_variable_containing_the_word_DELIMITER
but sticking it in a variable should work fine in all shells I know of
DELIMITER

줄이 끝에서 분리되는지 여부는 사용자가 제어할 수 없습니다.~ 할 것이다개행 문자로 끝납니다. 대부분의 경우 이는 귀하가 원하는 것일 수도 있고 중요하지 않을 수도 있습니다. 또한 많은(모두?) 쉘은 이 파일을 구현하기 위해 디스크의 임시 파일을 사용하므로 매우 제한된 시스템에서 이를 허용하지 않는 상황에 직면할 수 있습니다(끔찍하게 손상된 Android 인스턴스와 동일하지만 printfSELinux도 없습니다. 정책) 또는 다른 권한 제한(정확히 기억나지 않음)으로 인해 쉘이 임시 파일을 생성하지 못합니다.

echo따라서 컴퓨터 보안 참고 사항에서 민감한 정보를 인쇄해야 하는 경우 정확한 시스템에 따라 여기에 있는 파일이 그보다 나을 수도 있고 나을 수도 있습니다 ( echo외부인지 내장인지? /proc/$PID 세계인지 사용자인지) 여기 있는 파일을 사용자 또는 다른 사람이 읽을 수 있습니까?) 및 정확한 위협 모델(실행 중인 프로세스 정보보다 디스크에서 법의학적 검색을 수행할 가능성이 더 높습니까?)

expr

잘 알려지지 않은 기능은 expr정규식 일치를 통해 매개변수에서 하위 문자열을 추출하고 인쇄하는 기능입니다. 이는 기본적으로 원래 동작 echo(새 줄을 사용하여 내용을 그대로 인쇄)의 이식 가능한 버전이며 다음보다 일반 텍스트를 인쇄하는 보다 이식 가능한 방법입니다 printf.

expr X"$my_var_holding_my_text" : 'X\(.*\)'

그리고

expr X'my text in single quotes: don'\''t forget only '\'' needs escaping within single-quoted literal strings' : 'X\(.*\)'

이것은 Unix v7로 돌아갑니다. X인쇄할 문자열/변수 앞에그리고정규식 앞에외부하위 패턴이 일치/선택하는 값 \( \)은 중요합니다. 전자는 인쇄 중인 값이 명령에 의해 키워드 expr로 잘못 해석되는 것을 방지하는 반면, 후자는 X가 실제로 인쇄되지 않도록 보장합니다.expr

awk

다음은 awk수신되는 대부분의 단일 문자열 인수를 명시적으로 인쇄하는 간단한 한 줄입니다(최근 버전에서는 여전히 백슬래시 문제가 있습니다. awk주석에서 이 사실을 상기시켜 준 Stephan에게 감사드립니다).

: | awk 'BEGIN { ORS="" } END { print v }' v="$my_var_with_my_string"

이것은 Unix v7로 돌아갑니다. 백슬래시가 없으면 이식성이 매우 뛰어나며 출력해야 하는 텍스트에 충분할 수 있습니다. 또한 awk스크립트의 다양한 구현에 대한 기능 테스트를 작성하는 것이 자신에게 맞는 것보다 더 쉽고/간단하고/깨끗하다는 것을 알 수 있습니다 . 확실히 편차가 많지만 echo핵심 목표가 단지 일부 기능을 작성하는 것만 awk큼 좋지는 않기 때문입니다. , 테스트해야 할 변경 사항이 더 적습니다. echo정확한 출력.

변수 대신 리터럴을 사용하려면 작은따옴표로 묶인 문자열 기술을 사용할 수 있습니다. 그 뒤에 개행 문자를 추가하려면 echo인수 없이 작업을 수행하세요(또는 명령이 개행 문자를 인쇄하는지 확인하기 위해 특정 방법을 비판적으로 검토하는 데 시간을 투자하세요 . 왼쪽에 있는 no-op 명령을 바꾸는 것이 awk좋습니다). 인수가 없는 :파이프 echo이지만 I 이 아이디어는 완전한 이식성을 위해 신중하게 검사되지 않았습니다.)..

echosed파이프 또는 이와 유사한 방법을 통해

입력이 특별하지 않다는 것을 알고(문자 \000그대로 인쇄하려는 입력 과 같이 백슬래시 8진수 이스케이프가 없고 -문자를 구체적으로 구문 분석하지 않아야 하는 경우(예: 인쇄하려는 경우 ) 여전히 수행 -e할 수 있습니다. 다음을 사용하여 전처리할 수 있는 출력:echoecho

echo X-e | sed '1 s/^X//'

제한적이고 잘 정의된 입력의 경우 sed이와 같은 간단한 대체 방법으로 문제를 해결할 수 있습니다. 특정 요구 사항에 따라 점점 더 어려워질 수 있습니다. 어떤 시점에서는 다음 대안으로 넘어가는 것이 가장 좋습니다.

기능 검사echo

echo모든 수고를 다할 의향이 있으면 원하는 것을 안정적으로 인쇄할 수 없다는 생각이 반드시 사실인 것은 아닙니다. 특히 원하는 출력 세트가 잘 알려진 경우에는 더욱 그렇습니다. 저를 믿으십시오. 이것은 echo파일 시스템 어딘가에서 올바른 바이너리를 검색하는 것보다 훨씬 쉽습니다.

특히 안정적인 문자 인쇄에 대한 우려를 표명하셨습니다 -. 불행하게도 아직 완전한 기능 테스트 셸 스크립트 조각을 작성하지 않았지만 echo다음은 마음에 드는 몇 가지 기본 조각입니다.

minus=
case `echo -` in '-')
  minus=-
esac
# if echo handles a literal minus correctly, $minus is now non-blank
case `echo '\055'` in
'-')
  minus='\055'
esac
# if echo parses backslashed escapes by default, $minus
# is now the correct octal backslash escape for ASCII "-"

( 또는 echo -e '\055'출력해야 함 ), (기본적으로 백슬래시 이스케이프를 구문 분석하고 이를 끄려는 경우) 등과 같은 특정 항목에 대해 유사한 테스트를 구성할 수 있습니다.-e \055-echo -E '\055'

많은 최신 에코 인스턴스는 8진수 외에 다른 백슬래시 이스케이프를 구문 분석하지만 이들(또는 다른 것)에 대해 특별히 기능 테스트를 수행할 수 있습니다 echo '\x2d'. 콘텐츠를 특별히 대체하지 않고 콘텐츠를 인쇄한 다음 원하는 출력을 그대로 제공합니다.

필요에 따라 echo -n테스트할 가치가 있을 수도 있지만 명심하세요.명령 대체마지막 개행 문자는 항상 제거됩니다(대부분의 쉘에서는 마지막 개행 문자만, 일부 쉘에서는 모든 후행 개행 문자). 따라서 가능한 두 가지 출력 옵션은 리터럴 -n과 빈 문자열입니다.

내 생각에 이러한 도구는 에코를 찾기 위해 최선을 다할 것이며 유효한 것을 찾을 수 없거나 다른 것을 찾을 수 없는 경우 명시적인 인쇄를 수행하는 데 사용할 수 있기 때문에 리소스 를 참조 autoconf하고 얻을 수도 있습니다.m4printf

말 그대로 또 뭐야

솔직히 말해서 올바른 결과를 얻기 위해 무차별 대입 검색을 해야 하는 것에 의존하지 않는 것이 echo가장 좋을 것이라고 생각합니다. 특정 프로그램이 echo설치되지 않거나, 보이는 곳에 설치되지 않거나, 처음부터 자동화된 무차별 대입 검색으로 인해 /일부 불쌍한 사람의 시스템이 무릎을 꿇게 될 가능성이 높습니다 .

바이너리가 지문으로 GNU로 식별될 가능성은 거의 없지만 coreutils echo동작에는 차이가 있습니다. GNU가 구현을 전혀 변경하지 않더라도 누군가는 자신이 echo생각하는 작업을 수행하지 않도록 설치된 GNU 버전을 래핑할 수 있습니다. 이는 어리석은 행동입니다(특수 인수를 자동으로 제거하는 것을 제외하고 모든 인수를 투명하게 전달하는 동시에 원하는 대로 설정하는 것은 쉘 스크립트에서는 사소한 일이므로 echo --help올바른 텍스트를 쉽게 인쇄 할 수 있지만 echo -e '\055'잘못된 작업을 수행하게 됩니다). 아니 심지어바이너리철저한 지문 채취는 확실합니다. 동작을 변경하기 전에 원시 ELF 바이너리를 편집한 적이 있으며 다시 편집할 것입니다. 때로는 매우 유용한 기능을 활성화하기 위해(비공개 소스 메시징 소프트웨어의 유니코드 스마일리와 같이 비ASCII 바이트가 포함된 메시지를 자동으로 삭제하는 것 제외) 때로는 PS1셸의 하드코딩 기본값과 같은 매우 작은 기능을 위해 값이 다음으로 변경됩니다. 의 \$\) \w \$. 개인적으로 echo내가 실제로 사용하는 시스템에서는 echo거의 모든 심각한 작업 에 대해 이를 무시하기 때문에 이 작업을 수행할 충분한 이유가 없습니다 . 그러나 다른 사람들은 기본 변수 값에 대해 느끼는 것만큼 echo기본 동작에 대해 강하게 느낄 수도 있습니다. PS1이제 기능 테스트로 돌아왔습니다 echo. 이 시점에서 위 섹션을 참조하세요.

또한 내 시스템에 GNU coreutils가 echo설치되어 있으므로 gecho유효한 PATH설치 위치 및 이름이 지정된 파일에 대한 무차별 검색으로는 echo해당 시스템을 찾을 수 없습니다.

perl사실, GNU만 있는 시스템보다 원하는 것을 수행하는 일종의 스크립팅 언어가 설치된 시스템이 더 많을 것이라고 확신합니다 coreutils echo. 일부 스크립팅 언어는 어디에나 있고 대부분은 하나의 구현 또는 잘 정의된 사양을 가지고 있지만 echo구현은 무수히 많으며 " echo가능한 한 많은 다른 구현과 약간 다른 것을 수행하십시오"라는 하나의 사양을 엄격하게 준수합니다.

관련 정보