web-dev-qa-db-ja.com

printfが改行をエスケープしないのはなぜですか?

$ printf "hi"
hi$ printf "hi\n"
hi
$ printf "hi\\n"
hi

最終行が印刷されないのはなぜですかhi\n

6
Adam

これはprintfとは関係がなく、printfに指定した引数と関係があります。

二重引用符で囲まれた文字列では、シェルは\\\に変換します。したがって、printfに指定した引数は実際にはhi\nであり、もちろんprintfits ownエスケープシーケンス処理を実行します。

二重引用符で囲まれた文字列では、シェルによって\を介して行われるエスケープは、specifically、\`$、および"の文字にのみ影響します。 \nprintfにそのまま渡されることがわかります。したがって、printfに指定した引数は、実際にはhi\nagainです。

エスケープシーケンスをprintfのフォーマット文字列に入れる場合は注意してください。 一部のみSingle Unix Specificationで定義された意味があります。たとえば、\nは定義されていますが、\cは実際には定義されていません。

参考文献

31
JdeBP

二重引用符内では、\\nはエスケープされた(引用符で囲まれた)バックスラッシュの後にnが続きます。 \nprintfは改行を出力するため、これはprintfに渡されます。

二重引用符内で(まだ)、\nは文字列\nです。再び、printf\n文字列を受け取り、改行を出力します。

二重引用符内では、別のバックスラッシュ、改行、または$`、または"の前にある場合、バックスラッシュは特別なonlyです。 「特別」とは、次の文字の特別な意味を取り除くことを意味します。バックスラッシュが他の文字の前にある場合(たとえば、n)、それは単なるバックスラッシュ文字です。

これは POSIX標準 で説明されています。

\nprintf形式の文字列で出力するには、printf '\\n'またはprintf "\\\\n"を使用するか、printf '%s' '\n'を使用します

一般に、printfフォーマット文字列は一重引用符で囲み、変数データはフォーマット文字列に挿入する追加の引数として指定する必要があります。

printf 'This is how you write a newline: %s\n' '\n'
14
Kusalananda

では、別の視点を追加しましょう。

ここでは、2つのレベルの解釈があります。 1つはシェルで、もう1つは受け取った引数のコマンド(この場合はprintf)解釈です。

二重引用符の内側シェルはほとんどのシーケンスのバックスラッシュ文字をそのままにします。これは一般的な結果です:

$ printf '%s\n'    "\a \b \c \d ... \z     \$ \` \\ "
\a \b \c \d ... \z     $ ` \

Exceptwith $`、および\は、シェルにとって特別であり、それらの\を削除しました。

したがって、使用した両方の文字列(およびその他)をテストすると、次のようになります。

$ printf '%s\n'     "hi\n"     "hi\\n"    "hi\\\n"    "hi\\\\n"    "hi\\\\\n"
hi\n
hi\n
hi\\n
hi\\n
hi\\\n

シェルは、\\のペアを1つの\に変換します。そして、そのまま\n\nのままにします。

現在、printfは最初の引数と特別な関係にあり、the formatに明示的に設定されています。 format引数では、一部の文字は(printfにとって)特殊です。たとえば、%文字およびで始まる有効なシーケンス- バックスラッシュ文字のシーケンス のように:

\\  \a  \b  \f  \n  \r  \t  \v  and the special \ddd

したがって、文字列\nは改行を生成しますが、\\nは生成しません。

$ printf "    hi\n    hi\\n    hi\\\n   hi\\\\n"; echo
hi
hi
hi\n   hi\n
1
Isaac