/bin/sh는 문자열을 평가하지만 Bash는 그렇지 않습니다.

/bin/sh는 문자열을 평가하지만 Bash는 그렇지 않습니다.

sh아래 스크립트를 실행하면 사용된 셸이 다음 인지에 따라 두 가지 다른 출력이 표시됩니다 bash.

regex(){
     echo 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'
}

replace_builtins(){
    sed -e "$(regex)"
}

echo 'if !has(\"nvim\"): ' | replace_builtins
  • 세게 때리다:if !MOCK_has(\"nvim\"):
  • :??MOCK_has(\"nvim\"):

(이 물음표는 원래 터미널에서 그대로 복사되었지만 게시물을 저장하면 사라졌습니다. 기본적으로 인쇄할 수 없는 문자입니다.)

이 동작을 설명하기 위해 POSIX sh 모드에서 실행할 때 어떤 일이 발생하는지 알고 싶습니다.

편집: 보너스 포인트의 경우 echo함수 내에서 교체할 때 Bash에서도 이런 일이 발생하는 이유를 설명하십시오.printfregex

     printf 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'

답변1

설명은 에 있어요POSIX 사양echo:

표준 출력에 기록될 문자열입니다. 첫 번째 피연산자가 -n이거나 피연산자에 <backslash> 문자가 포함된 경우 결과는 구현에 따라 정의됩니다.

POSIX는 주로 역사적 관행을 성문화하며 때로는 역사적 관행이 일관성이 없습니다. 일부 쉘은 인수의 이스케이프 시퀀스를 echo예를 들어 \t탭 및 \1바이트 값이 1()인 문자로 확장합니다 ^A. 다른 쉘에서는 백슬래시를 일반 문자로 처리합니다.

임의의 문자열을 인쇄하는 이식 가능한 방법은 항상 첫 번째 인수(형식)에서 백슬래시 이스케이프 시퀀스를 확장하는 를 사용하는 것입니다 printf. printf문자열을 문자 그대로 인쇄하려면 다음을 사용하십시오.

printf %s 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'

문자열을 문자 그대로 인쇄하고 끝에 개행 문자를 추가하려면 다음을 사용하십시오.

printf '%s\n' 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'

쉘 스크립트에서 작은따옴표 리터럴을 사용하여 문자열을 작성하는 경우 작은따옴표 문자를 로 작성해야 합니다 '\'''. 이것은 쉘 구문에 대한 질문이며 문자 그대로 문자열을 인쇄하는 것과는 완전히 다른 질문입니다.

답변2

흥미로운 질문을 찾았습니다...

문제는 POSIX 호환성이 부족하다는 것입니다 dash.

POSIX 소규모 임베디드 시스템과 Linux와 같이 UNIX 호환성을 요구하는 대규모 시스템을 구별하는 기본 POSIX 호환성 수준입니다. 후자의 경우 시스템은 소위 XSI 확장을 모두 구현해야 합니다.

XSI 호환 시스템에는 echo확장 매개변수에서 일부 백슬래시 이스케이프가 필요합니다.

bashbashPOSIX/XSI 호환 동작으로 컴파일하는 것이 가능하지만(예: Solaris 및 MacOS에서 수행됨) Linux의 바이너리는 이를 수행하지 않습니다. POSIX/XSI 준수를 위해 컴파일된 경우 매개변수의 백슬래시 이스케이프를 bash올바르게 처리하고 샘플 코드 이스케이프 시퀀스에 POSIX/XSI가 없기 때문에 echo샘플 코드는 Solaris 또는 MacOS의 해당 바이너리에 대해 작동합니다 .bash

XSI는 Linux에서 호환되지 않기 때문에 bash매개변수의 백래시 이스케이프를 전혀 확장하지 않습니다. 이것이 바로 예제 코드가 echoLinux에서도 작동하는 이유입니다.bash

dash상대방은 POSIX/XSI 준수를 주장하고 echo매개변수의 백슬래시 이스케이프를 확장합니다. POSIX/XSI 준수가 올바르게 구현되면 샘플 코드 dash도 작동합니다 . dash이는 샘플 코드에 POSIX/XSI 백슬래시 이스케이프 시퀀스가 ​​포함되어 있지 않기 때문입니다.

POSIX/XSI에는 echo확장이 필요합니다.

\0nnn  for an octal number that represents the related character

예제 코드에는 백슬래시 시퀀스가 ​​포함되어 있습니다.

\1 for the first sed subexpression

그리고

\2 for the second sed subexpression

echo그리고 이는 POSIX/XSI 이스케이프 시퀀스의 일부가 아니므로 POSIX 호환 셸의 내장 함수는 이를 확장 할 수 없습니다 . 그러나 POSIX에서는 8진수가 잘못 확장되므로 이를 금지합니다. 이것이 예제 코드가 실패하고 표시되는 이유입니다.echodash\1\2dash

버그 보고서를 제출 dash하고 수정될 echo argprintf '%s\n' arg까지 기다리 dash거나 .printfdash

따라서 POSIX/XSI 오류를 나열할 수 있습니다 dash.

  • 멀티바이트 문자는 지원되지 않습니다.

  • 금지된 경우에도 \nnn매개변수에서 확장됩니다.echo

  • 이것이 필요한 경우에도 \nnn매개변수는 확장되지 않습니다.printf

관련 정보