printf

printf

in echo은 어디에나 존재하는 것처럼 보이지만 모든 시스템이 동일한 위치(보통 ) coreutils에 있는 것은 아닙니다 . 그것이 어디에 있는지 모르고 /bin/echo이것을 호출하는 가장 안전한 방법은 무엇입니까 ?echo

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

참고: 여기서 동기는 바이너리 를 찾는 것입니다 echo.~ 아니다모든 쉘의 인수 집합을 찾으려면echo 내장일관성이 있습니다. 예를 들어, zsh또는 에 있는지 알지 못한 채 echo 내장을 통해 하이픈만 안전하게 인쇄하는 방법은 없는 것 같습니다 bash.

답변1

이는 coreutilsGNU 시스템에 Unix 기본 유틸리티 세트를 제공하기 위해 GNU 프로젝트에서 개발한 소프트웨어 번들입니다. 당신은 단지 찾을 것입니다코어 유틸리티echoGNU 시스템에서 바로 사용할 수 있습니다( Debian, trisquel, Cygwin, Fedora, CentOS...). 다른 시스템에서는 다른 echo구현(일반적으로 이식성이 가장 낮은 응용 프로그램 중 하나와 다른 동작)을 찾을 수 있습니다 . FreeBSD에는 FreeBSD가 있고 echo, 대부분의 Linux 기반 시스템에는 busybox가 있으며 echo, AIX에는 AIX가 있습니다 echo.

일부 시스템에는 하나 이상의 시스템도 있습니다(예: /bin/echo/usr/ucb/echoSolaris(후자는 /usr/gnu/bin/echo다른 CLI를 사용하는 GNU 유틸리티 패키지와 같은 이후 버전의 Solaris에서 선택 사항인 패키지의 일부임)와 같습니다).

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

또한 의 버전 간 비호환성을 발견할 수 있으며 (예를 들어 의 시퀀스를 coreutils echo인식하지 못함 ) 해당 동작이 환경( 변수)에 의해 영향을 받을 수 있다는 점에 유의하세요.\x41-ePOSIXLY_CORRECT

이제 다른 모든 내장 기능과 마찬가지로 echo파일 시스템( 을 검색하여 찾은)에서 를 실행하려면 일반적인 방법은 다음과 같습니다 .$PATHenv

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 호환 버전을 제공합니다 .

이는 인쇄 가능한 문자열 "GNU coreutils"와 "Echo the STRING(s) to Standard Output"을 모두 포함하는 실행 파일을 찾습니다. 이 문자열은 GNU echo--help출력에 표시되고 문자 그대로 프로그램 텍스트에 포함됩니다. 복사본이 두 개 이상 있으면 찾은 첫 번째 복사본을 임의로 선택합니다. 아무것도 발견되지 않으면 실패합니다. $(...)빈 문자열로 확장됩니다.


하지만 "안전하다"고는 할 수 없습니다. 시스템의 어느 곳에나 이 (실행 가능한) 스크립트가 있으면 문제가 발생할 수 있기 때문입니다.

#!/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를 사용합니다.

에서 인용§11.14 쉘 내장의 제한 사항~의autoconf 매뉴얼:

에코

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

인수 처리에 대한 합의가 없으므로 인수에 백슬래시를 사용하지 마십시오. 의 echo '\n' | wc -l경우sh솔라리스출력 2하지만세게 때리다그리고Zsh( sh에뮬레이션 모드에서) 출력 1. 문제는 정말로 echo모든 쉘이 '\n'백슬래시와 n. 명령 대체 내에서 echo 'string\c'내부 상태가 엉망이 됩니다.ksh88~에AIX 6.1그래서 첫 번째 문자 s만 인쇄하고 그 뒤에 개행 문자를 인쇄한 다음 명령 대체에서 다음 에코의 출력을 완전히 삭제합니다.

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

이것이 사실이 아닐 경우 printf일반적으로 echo및 보다 사용하기가 더 안전하고 쉽습니다 echo -n. 따라서 이식성이 주요 관심사가 아닌 스크립트는 실패할 수 있을 printf '%s\n'때마다 사용해야 하며 마찬가지로 대신 . 이식 가능한 쉘 스크립트의 경우 대신 다음과 같은 여기 문서를 사용하는 것이 좋습니다.echoprintf %secho -n

          cat <<EOF
          $foo
          EOF

답변4

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

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

printf

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

관점에서 볼 때 저는 쉘 스크립트 이식성이 건강에 해롭다는 점에 집착하고 있으며 문자 그대로만 액세스할 수 있습니다.현재 Bourne과 같은 셸이 있는 시스템에는 printf가상화된 Unix v7(예, 약 40년 전의 것)과 기본적으로 다음을 갖춘 Android 장치(내 소유 약 5개 중)가 있습니다.아무것도 아님설치되어 있고 너무 잠겨 있어서 어쨌든 조만간 유용한 쉘 스크립트를 실행하지 못할 것입니다.

그러면 문자열이 인쇄됩니다.정확히, on - 약속합니다 - 현대에 누구나 사용할 수 있는 모든 시스템은 다음과 같습니다.

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진수 이스케이프를 사용 하고 이를 0개 이상의 %s추가 인수와 결합하여 다른 모든 텍스트를 인쇄합니다. 내가 아는 한 16진수 이스케이프(8진수 대비) 및 기타 트릭은 이식성이 떨어집니다.

제안: 넣지 마세요아무것당신은하지 않습니다필요특별히 형식 문자열로 구문 분석/변환되었습니다. 다양한 printf구현은 약간 다른 형식을 지원합니다(최신 printf구현 포함, 예: bash내장 vs busybox 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

한 가지 주의할 점은 끝에 개행 문자가 나오는지 여부를 제어할 수 없다는 것입니다.~ 할 것이다끝에 개행 문자를 얻으세요. 대부분의 경우 이는 아마도 귀하가 원했던 것일 수도 있고 중요하지 않을 수도 있습니다. 또한 많은(모두?) 쉘은 디스크의 임시 파일을 사용하여 here-file을 구현하므로 매우 제한된 시스템에서 이를 허용하지 않는 상황에 직면할 수 있습니다( printf나 없이도 외설적으로 손상된 동일한 Android 인스턴스에는 SELinux도 있습니다) 정책 또는 쉘이 임시 파일을 생성하는 것을 방지하는 기타 권한 제한(정확히 기억하지 못함).

이 때문에 컴퓨터 보안 참고 사항에서 민감한 정보를 인쇄해야 하는 경우 echo정확한 시스템에 따라 here 파일이 보다 나을 수도 있고 나을 수도 있습니다( 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로 잘못 해석되는 것을 방지 expr하고 후자는 X가 실제로 인쇄되지 않도록 합니다.

awk

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

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

이것은 Unix v7에서 다시 작동합니다. 백슬래시가 없으면 이식성이 매우 뛰어나며 출력해야 하는 텍스트에 충분할 수 있습니다. awk스크립트에서 다양한 구현에 대한 기능 테스트를 작성하는 것이 echo작업을 수행하는 것보다 더 쉽고/간단하고/깔끔하다는 것을 알 수 있습니다. 왜냐하면 s들 사이에는 분명히 많은 편차가 있지만 핵심 목표가 단지 일부를 작성하는 것 awk보다 테스트할 변형이 적기 때문입니다. echo정확한 출력.

변수 대신 리터럴을 사용하려면 작은따옴표로 묶은 문자열 기술을 사용하십시오. echo그 뒤에 줄바꿈을 원하면 인수 없이 수행하십시오 (또는 명령에 의해 줄바꿈이 인쇄되도록 특정 방법을 엄격하게 조사하는 데 시간을 투자하십시오. 파이프 왼쪽의 no-op 명령을 인수 없이 awk바꾸는 것이 좋습니다 . 그러나 나는 전반적인 이식성에 대한 아이디어를 신중하게 감사하지 않았습니다.):echo

echo파이프를 통해 sed또는 이와 유사한

입력이 특별하지 않다는 것을 알고( \000문자 그대로 인쇄하려는 입력에서와 같이 백슬래시 8진수 이스케이프가 없고 문자를 특별히 구문 분석하는 것을 피해야 하는 경우 -(예: print ) 에 대해 임의의 작업을 -e수행할 수 있습니다. 의 출력을 echo전처리하는 데 사용할 수 있는 다른 것이 있는 경우 :echo

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'(기본적으로 백슬래시 이스케이프를 구문 분석하고 이를 끄려는 경우) 등

echo의 많은 최신 인스턴스는 8진수 이외의 다른 백슬래시 이스케이프를 구문 분석하지만 해당 기능( echo '\x2d'또는 무엇이든)에 대해 특별히 기능 테스트를 수행할 수 있습니다. 하지만 대부분의 경우 실제로 전달할 수 있는 인수 집합을 찾고 싶을 것입니다. 내용을 특별히 대체하지 않고 내용을 인쇄하도록 에코한 다음 원하는 출력을 그대로 제공합니다.

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

또한 해당 도구는 작동하는 다른 것을 찾을 수 없는 경우 명확한 인쇄를 수행하는 데 사용할 수 있는 에코를 찾기 위해 노력하고 있다고 생각하기 때문에 상담 autoconf하고 소스를 원할 수도 있습니다 .m4printf

말 그대로 다른 것

나는 진심으로 당신이 정확히 옳은 것을 찾기 위해 무차별 대입 검색을 해야 하는 것에 의존하지 않는 모든 것이 echo최선이라고 생각합니다. 특정 echo항목이 설치되지 않거나, 보이는 곳에 설치되지 않을 가능성이 매우 높으며 , 자동 무차별 대입 검색으로 인해 /일부 불쌍한 녀석의 시스템이 크롤링 상태로 떨어질 가능성이 매우 높습니다.

가능성은 매우 낮지만 바이너리가 GNU라는 지문 인식을 통과할 가능성이 있지만 coreutils echo동작에 차이가 있을 수 있습니다. GNU가 구현을 전혀 변경하지 않더라도 누군가 자신이 설치한 GNU 버전을 래핑하여 echo고려한 작업을 수행하지 않을 수도 있습니다. 멍청한 행동을 하십시오(원하는 것을 설정하는 동안 특별한 것을 자동으로 삭제하는 것을 제외하고 모든 인수를 투명하게 전달하는 것은 쉘 스크립트에서는 사소한 일이므로 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가능한 한 많은 다른 구현과 약간 다른 것을 수행합니다 ".

관련 정보