이 질문은 다음과 관련이 있습니다.printf를 사용하여 백슬래시와 개행 문자를 인쇄하는 방법은 무엇입니까?, OP는 \\\n
단일 백슬래시와 개행 문자(리터럴이 아님 \n
)로 인쇄하려고 합니다.
\\
쉘 규칙에 따라 확장하는 것이 합리적 \
이지만 (즉, 쉘은 후속 문자의 리터럴 형식을 보존하기 위해 백슬래시 이스케이프를 수행합니다), 이를 실행하면 쉘이 완전히 다른 작업을 수행하는 것처럼 보입니다. 내가 보고 있는 것을 설명하기 위해.\n
n
strace
$ 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
섹션의 인수를 백슬래시 및 개행 문자로 해석합니다. 또한 쉘은 사용자가 입력한 내용을 자체 규칙과 일치하도록 변환해야 하지만 여러 백슬래시에서 정확히 무슨 일이 일어나고 있는지 말로 표현하는 데 어려움을 겪고 있습니다. argv
execve()
\\\n
답변1
strace
\\
단일 백슬래시가 로 표시되고 개행 문자가 로 표시되는 C 문자열 구문으로 문자열을 표시합니다 \n
.
전달되는 것은 execve
C 소스 코드에서 인수로 인쇄된 문자열 리터럴이 주어졌을 때 함수가 인쇄할 내용입니다.puts
strace
답변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
이는 귀하의 예에 표시된 것과 일치합니다.