bash 규칙을 통해 감소하는 대신 strace의 백슬래시 수가 증가하는 이유는 무엇입니까?

bash 규칙을 통해 감소하는 대신 strace의 백슬래시 수가 증가하는 이유는 무엇입니까?

이 질문은 다음과 관련이 있습니다.printf를 사용하여 백슬래시와 개행 문자를 인쇄하는 방법은 무엇입니까?, OP는 \\\n단일 백슬래시와 개행 문자(리터럴이 아님 \n)로 인쇄하려고 합니다.

\\쉘 규칙에 따라 확장하는 것이 합리적 \이지만 (즉, 쉘은 후속 문자의 리터럴 형식을 보존하기 위해 백슬래시 이스케이프를 수행합니다), 이를 실행하면 쉘이 완전히 다른 작업을 수행하는 것처럼 보입니다. 내가 보고 있는 것을 설명하기 위해.\nnstrace

$ strace -e execve printf "\\\n"
execve("/usr/bin/printf", ["printf", "\\\\n"], [/* 42 vars */]) = 0
\n+++ exited with 0 +++

즉, 내가 보고 있는 것은 argv시스템 호출 부분에 들어가는 문자 ​​수가 execve감소하는 대신 추가 백슬래시가 추가되어 실제로 증가한다는 것입니다.

작은따옴표를 전달하는 것은 '\\\\n'훨씬 더 혼란스럽습니다.

$ strace -e execve printf '\\\\n'
execve("/usr/bin/printf", ["printf", "\\\\\\\\n"], [/* 42 vars */]) = 0
\\n+++ exited with 0 +++

즉, 여기의 셸이 이전 명령에서 변경되지 않은 printf모든 것을 전달 execve()하고 와 동일한 출력을 얻도록 하고 싶지만 printf "\\\n"다릅니다.

어떤 면에서 나는 이해를 중심으로 머리를 감싸는 것 같습니다.순수한 printf자체적으로(시스템에 의해 실행됨) \\\n섹션의 인수를 백슬래시 및 개행 문자로 해석합니다. 또한 쉘은 사용자가 입력한 내용을 자체 규칙과 일치하도록 변환해야 하지만 여러 백슬래시에서 정확히 무슨 일이 일어나고 있는지 말로 표현하는 데 어려움을 겪고 있습니다. argvexecve()\\\n

답변1

strace\\단일 백슬래시가 로 표시되고 개행 문자가 로 표시되는 C 문자열 구문으로 문자열을 표시합니다 \n.

전달되는 것은 execveC 소스 코드에서 인수로 인쇄된 문자열 리터럴이 주어졌을 때 함수가 인쇄할 내용입니다.putsstrace

답변2

적용되는 유일한 이스케이프는 인수를 호출 프로그램에 전달하기 전에 인수를 이스케이프 해제하는 것이므로 \\( \큰따옴표로 묶인 문자열에 적용되는 몇 가지 이스케이프 규칙 중 하나) 쉘은 큰따옴표로 묶인 문자열에 대해 거의 아무것도 수행하지 않습니다. 작은따옴표로 구분된 문자열입니다.

strace반면에 C 소스 코드와 대략 비슷해 보이는 것을 작성하려고 하면(정확히 C 언어 함수 호출의 모양은 아니지만) 모든 \s가 다시 이스케이프됩니다.

그래서:

  • 존재하다
    % strace -e execve printf '!\n'
    execve("/usr/bin/printf", ["printf", "!\\n"], [/* 35 vars */]) = 0
    !
    +++ 0으로 종료++++
    %
    strace에 전달되고 이후에 전달되는 인수는 printf정확히 세 개의 문자열입니다 !\n. strace이는 C 언어 문자열 상수로 인쇄되며, \문자열은 내부적으로 두 배가 되어 결과적으로 개행 문자로 해석됩니다 ".\\n".printf\n
  • 존재하다
    % strace -e execve printf '!\\n'
    execve("/usr/bin/printf", ["printf", "!\\\\\\n"], [/* 35 vars */]) = 0
    ! \
    +++ 0으로 종료++++
    %
    \C 문자열에 두 배로 늘려야 할 s가 더 많고 나중의 s가 printf인식된다는 점을 제외하면 똑같은 일이 일어나고 있습니다 .\\\n
  • 존재하다
    % strace -e execve printf '!\\\\n'
    execve("/usr/bin/printf", ["printf", "!\\\\\\\\n"], [/* 35 vars */]) = 0
    !\\n+++ 0으로 종료 +++
    %
    \C 문자열에 두 배로 늘려야 할 s가 더 많고 뒤따르는 s가 printf인식된다는 \\점 을 제외하면 똑같은 일이 일어나고 있습니다 .\\n
  • 존재하다
    % strace -e execve printf \!"\\\\n"
    execve("/usr/bin/printf", ["printf", "!\\\\n"], [/* 35 vars */]) = 0
    !\n+++ 0으로 종료 +++
    %
    !\\n쉘 언어에서 큰따옴표로 묶인 단어에 대한 이스케이프 규칙은 C 언어 문자열의 인식을 방지하기 위해 다음 과 같이 \\표시 됩니다 .\!"!\\\\n"printf\\n
  • 존재하다
    % strace -e execve printf \!$'\007'"\\\\n"
    execve("/usr/bin/printf", ["printf", "!\7\\\\n"], [/* 35 vars */]) = 0
    !\n+++ 0으로 종료 +++
    %
    문자의 C-문자열 이스케이프 형식이 세 번째 인용 형식을 사용하는 쉘의 형식과 완전히 다르게 보이는 점을 제외하면 거의 동일한 일이 발생합니다 .

답변3

다른 사람들이 언급했듯이 strace인쇄 시스템 호출 매개변수는 C에서 특수 문자를 참조하는 것과 같은 방식으로 참조됩니다. 개행 문자는 \n, 리터럴 백슬래시는 \\, a 등 으로 표시됩니다. (매뉴얼 페이지)

문자 포인터는 역참조되어 C 문자열로 인쇄됩니다. 문자열에서 인쇄되지 않는 문자는 일반적으로 일반 C 이스케이프 코드로 표시됩니다.

set -x셸이 실행 중인 명령에 보내는 내용을 확인하는 것이 더 쉬울 수 있습니다. Bash는 xtrace 출력을 작은따옴표로 묶어서 백슬래시가 효과가 없습니다.

첫 번째 예에서는 다음과 같습니다.

$ set -x
$ printf "\\\n" > /dev/null
+ printf '\\n'

첫 번째 백슬래시는 두 번째 백슬래시를 이스케이프하여 단일 \. 편지는 문자 그대로 받아들여지므로 \\n. C 따옴표, 이중 백슬래시.

큰따옴표 안의 백슬래시 이스케이프 문자표준에 명확히 기재되어 있음, 이는 달러 기호 $, 백틱 표시 `, 큰따옴표 ", 백슬래시 자체 \및 개행 문자입니다.

느낌표는 역사적 확장성 때문에 특별하지만, 이스케이프하면백슬래시가 앞에 있습니다.아니요Bash에서 삭제. 그러나 기록 확장이 활성화된 경우 Zsh는 이를 제거합니다.

답변4

쉘은 백슬래시 수를 줄입니다(한 가지 경우). 그러나 execve "C-quoting"에서는 이중 백슬래시로 표현됩니다.

큰따옴표(대시)로 묶습니다.

큰따옴표 문자를 큰따옴표로 묶으면 달러 기호($), 백틱(`) 및 백슬래시(\)를 제외한 모든 문자의 리터럴 의미가 유지됩니다. 큰따옴표 안의 백슬래시는 역사적으로 이상하며 다음 문자를 인용하는 데에만 사용되었습니다:
$ ` " \ <newline>.
그렇지 않으면 리터럴로 유지됩니다.

따라서 이 줄에서는 다음과 같습니다.

$ strace -e execve printf "\\\n"

쉘은 매개변수를 약간 변경하며 이것이 strace가 수신하는 것입니다:

$ strace -e execve printf "\ \n"     # Space added for emphasis, not real.

첫 번째 백슬래시만 다음 백슬래시를 참조합니다. 그런 다음 문자열이 "C 인용" 문자열로 표시되는 execve의 인용 메커니즘은 사용되는 백슬래시 수를 2에서 4로 두 배로 늘립니다.

$ execve("/usr/bin/printf", ["printf", "\\\\n"], [/* 42 vars */]) = 0

이것이 보이는 것입니다.

작은따옴표로 묶음(man dash):

작은따옴표
문자를 작은따옴표로 묶으면 모든 문자의 문자 그대로의 의미가 유지됩니다(작은따옴표로 묶인 문자열 안에 넣을 수 없는 작은따옴표 제외).

따라서 strace는 다음과 같은 문자열을 받습니다.

strace -e execve printf '\ \ \ \ n'    #space(s) added for emphasis.

execve가 "C 인용문"을 만들면 4개의 백슬래시가 8이 됩니다.

execve("/usr/bin/printf", ["printf", "\\\\\\\\n"], [/* 42 vars */]) = 0

이는 귀하의 예에 표시된 것과 일치합니다.

관련 정보