この質問は以下に関連しています。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
適用される唯一のエスケープは、引数を呼び出し元プログラムに渡す前に引数をエスケープ解除することです\\
(\
二重引用符で囲まれた文字列に適用されるいくつかのエスケープ規則の1つ)、シェルは二重引用符で囲まれた文字列に対してほとんど何もしません。一重引用符で区切られた文字列。
strace
一方、Cソースコードとほぼ同じように見えるものを作成しようとすると(正確にC言語関数呼び出しの外観ではありませんが)、すべての\
sが再びエスケープされます。
だから:
- 存在する
% strace -e execve printf '!\n' execve("/usr/bin/printf", ["printf", "!\\n"], [/* 35 vars */]) = 0 ! +++ 0で終了++++ %
strace
に渡された後に渡される引数は、printf
正確に3つの文字列です!\n
。strace
これはC言語の文字列定数として印刷され、文字列は\
内部で2倍になり、結果として改行文字として解釈されます".\\n"
。printf
\n
- 存在する
% strace -e execve printf '!\\n' execve("/usr/bin/printf", ["printf", "!\\\\\\n"], [/* 35 vars */]) = 0 ! \ +++ 0で終了++++ %
\
C文字列に2倍に増やす必要があるsが多く、後のsが認識されることを除いて、同じprintf
ことが起こっています。\\
\n
- 存在する
% strace -e execve printf '!\\\\n' execve("/usr/bin/printf", ["printf", "!\\\\\\\\n"], [/* 35 vars */]) = 0 !\\n+++ 0 で終了 +++ %
\
C文字列に2倍に増やす必要がある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番目の引用形式を使用するシェルの形式とまったく異なるように見えることを除いて、ほぼ同じことが起こります。
答え3
他の人が述べたように、strace
印刷システム呼び出しパラメータはCで特殊文字を参照するのと同じ方法で参照されます。改行文字は\n
、リテラルバックスラッシュは\\
、aなどで表示されます。 (マニュアルページ)
文字ポインタは逆参照され、C文字列として印刷されます。文字列から印刷されない文字は通常、通常のCエスケープコードで表されます。
set -x
シェルが実行中のコマンドに送信する内容を確認する方が簡単です。 Bashはxtrace出力を一重引用符で囲み、バックスラッシュは効果がありません。
最初の例では、次のようになります。
$ set -x
$ printf "\\\n" > /dev/null
+ printf '\\n'
最初のバックスラッシュは、2番目のバックスラッシュをエスケープすることによって単一\
。手紙は文字通り受け入れられるので\\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に2倍にします。
$ 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
これはあなたの例に示されているものと一致します。