web-dev-qa-db-ja.com

ヌル文字とエスケープ文字

Linuxのファイル名には、2つの文字'/''\0'を除いて、何の制限もないことを知っています。 '/'はディレクトリセパレータであるため禁止されていることは知っていますが、他の理由がありますですか?

また、私の端末では、\0という名前のファイルまたはディレクトリを作成できます。それで、私はnull文字を正しく書き込む方法を疑問に思います

mkdir '\0'は、\0という名前のディレクトリを作成します

もう1つの質問、ファイル名に$を含める場合は、バックスラッシュを使用できます

mkdir \$myfileは、$myfileという名前のディレクトリを作成します

ただし、ドル記号を一重引用符と二重引用符で囲むと、同じことができます

mkdir \$myfilemkdir '$'myfilemkdir "$"myfilemkdir '$myfile'mkdir "$myfile"と同じです

だから私の質問は、一重引用符と二重引用符はエスケープのバックスラッシュ文字の代用ですか?

$、(スペース)、バックスラッシュ以外に、bashでエスケープする必要がある他の文字は何ですか?

7
alkabary

ヌル文字を印刷する

最近の多くのシェルでは、ドル記号の単一引用符形式$'\0'、16進数形式\x00、Unicode形式\u0000または\U00000000でnull文字を書き込むことができます。 '\0'。ポイントは、コマンドがバックスラッシュでエスケープされた文字をどう処理するかを理解する必要があるということです。たとえば、echoの場合は通常、-eオプションを追加する必要があり、printfの場合は%bになります。

動作するかどうか確認してみましょう:

$ echo -ne '\0'
$

したがって、echo -ne ''と同様に、何も生成しません。

$ printf '%b' '\0'
$

いくつかの文字を追加してみましょう(これからはより堅牢なprintf '%b'を使用しますが、echo -neでも同様の効果があります):

$ printf '%b' a'\0'b
ab

nullはどこに行ったのですか?

$ printf '%b' a'\0'b | wc -c
3

a''bと比較してみましょう。

$ printf '%b' a''b | wc -c
2

最後に、ファイルを作成する前に本当にnull文字を出力していることを確認し、表示された値をxargsのようなエラーをスローするコマンドに渡します。

$ printf '%b' a'\0'b | xargs echo
xargs: Warning: a NUL character occurred in the input.  It cannot be 
passed through in the argument list.  Did you mean to use the --null option?
a

最後にaだけが出力されていることに注意してください。もちろんxargs -0は問題なく動作します:

$ printf '%b' a'\0'b | xargs -0 echo
a b

Nullでファイルを作成しますか?

次に、null文字を含むファイルを作成してみましょう。

$ touch $'\0'
touch: cannot touch ‘’: No such file or directory
$ mkdir $'\0'
mkdir: cannot create directory ‘’: No such file or directory

# let's try another approach - using printf in command substitution:
$ touch "$(printf '%b' '\0')"
touch: cannot touch ‘’: No such file or directory
$ mkdir "$(printf '%b' '\0')"
mkdir: cannot create directory ‘’: No such file or directory

結果はtouch ''とまったく同じですが、nullはすべて一緒に無視されるようです。コマンド置換の前後の二重引用符をスキップするとどうなりますか?

$ touch $(printf '%b' '\0')
touch: missing file operand
Try 'touch --help' for more information.
$ mkdir $(printf '%b' '\0')
mkdir: missing operand
Try 'mkdir --help' for more information.

これは、引数なしのtouch/mkdirと同じ状況です。さらに別の結果は、ヌルをテキストで囲む場合です。

$ touch "$(printf '%b' a'\0'b)"
$ ls
a   # in zsh
ab  # in bash

また、標準出力を$'\0'にリダイレクトしようとすることもできますが、すべてのエラーは別の種類のエラーです。

9
jimmij

単一引用符/二重引用符とバックスラッシュの比較:単一引用符とバックスラッシュは引用力で同等です。スペース、タブ、改行、_()[]*$><?|{}~&;\"`^!#_、そしておそらく私が忘れている他の文字を含む長い文字列を引用するには、単一引用符を使用する方がはるかに便利です。ただし、バックスラッシュだけでまったく同じ結果を得ることができます(ただし、バッククォート内のバックスラッシュのオーバーロードに注意してください(_`...`_)。)

ただし、二重引用符は一意です。 _$_は二重引用符内で展開されますが、単一引用符ではありません。 "$ foo"はfooを展開しますが、展開された結果をワード分割およびグロブ展開から保護します。

http://mywiki.wooledge.org/BashFAQ から始めるのが良いでしょう。 bashマニュアルでは、説明されているすべての機能の使用方法に多くの時間を費やしていません。個別にどのように機能するかだけです。


ゼロバイトを含む文字列をコマンドライン引数として渡すこと、またはシステムコールに文字通り渡すことは不可能です。プロセスとカーネルの間でデータを渡す方法を正確に指定するABI(アプリケーションバイナリインターフェース)は、コマンドライン引数やシステムコールへのファイル/パス引数など、すべて(バイナリデータを除く)にC文字列を使用します。 C文字列は文字列の終わりで、文字列の終わりがゼロバイトでマークされています。文字列の終わりではないことを示すためにゼロバイトを「エスケープ」する方法はありません。

_touch $'foo\0bar'_のようなことをしようとすると、touchは引数リストを

_argv[0] = "/bin/touch";
argv[1] = "foo";
_

メモリに座っている場合でも、_argv[1] = "foo\0bar\0"_、最初の_\0_は文字列の終わりを示します。実際、 "foo\0bar\0"は、新しいプロセスのargvまでは到達しません。 touchを実行したexevce(2)システムコールのargv配列からは作成されません。

そして、ヌルバイトを含む文字配列/文字列でCまたはPerlプログラムを書いたとしても、それらをopen(2)のようなシステムコールに渡すと、カーネルによる文字列の同じ解釈が引き起こされます。 read(2)write(2)などの任意のデータを処理する必要があるシステムコールは、長さの引数とバッファへのポインタを受け取ります。


Bashでnullバイトを使用して多くのことを行うことは不可能です。 jimmijが指摘するように、エスケープシーケンス処理で文字列リテラルを書き込むためのbash構文は_$'string'_ですが、文字列リテラルに_\0_を書き込むと、bash内で文字列ターミネーターとして機能します。これは、bashが文字列を明示的な長さではなく、C文字列として内部的に格納することを意味すると思います。

_str=$'foo\0bar'
echo "${#str}"   # 3, showing that bash isn't even storing it in a variable.
echo "$str" | wc -c   # 4. wouldn't work even if ${#str} did: echo's cmdline would eat it
wc -c <<< $'foo\0bar'   # 4 (includes a newline)
_

したがって、この構文を使用してヌルバイトを送信することはできません。 trなどを使用する必要があります。

ただし、jimmijが指摘するように、_printf '%b' 'foo\0bar'_を使用して、nullバイトをstdoutに出力できます。

5
Peter Cordes

ご存じのとおり、_$var_は変数の解釈につながります。さまざまなオプションが機能する理由はさまざまです。

  • エスケープ(_\$var_):次の文字をシェルの機能文字として解釈しません。しかし、いくつかのケースでは:特別な意味を与えます(例:一部のコンテキストでは、改行の_\n_)
  • 一重引用符(_'$var'_):一重引用符内のすべては、厳密にそれらが含む文字列にすぎません
  • _$_(_"$"var_)の分離:単一の_$_は二重引用符で囲まれ、var部分から分離されて解釈されないため、解釈されません
  • 二重引用符(_"$var"_):変数varの解釈を実際に許可します:_mkdir "$var"_機能せず、IS他のものと同じではありません!もう一度確認してください!ただし、引用符内に含まれているものはすべて単一の文字列として扱われます。ファイル名に特殊文字がある場合、たとえば名前にスペースを含むファイルを作成する場合に特に役立ちます:_touch "a b"_->単一ファイル_a b_作成/更新、_touch a b_-> 2つのファイルaおよびb作成/更新。

その他の特別な演算子は次のとおりです:リダイレクトと「ここ」_> >> < << <<<_、プロセス演算子_& |_、ブール演算子_|| &&_、およびコマンド区切り記号_;_と括弧によるグループ化_( )_、時々-しかし、その後分離されるか、最初の文字として-stdinまたはコマンドのオプションの_-_。また、テストコマンド_[_と、すでに使用した引用符_' "_があります。また、感嘆符_!_で以前のコマンドを呼び出すか、ハッシュ_#_でコメントを呼び出します。ワイルドカードアスタリスク_*_および疑問符_?_(複数の単一文字の場合)。また、現在のディレクトリと親ディレクトリは_._と_.._ですが、ホームは_~/_に設定されています。つまり文字; & | > < - [ \ ' " ( ) # * ! ? . ~ ^ { }、_`_、改行、スペース、タブ(およびシングルバイトロケールのその他の空白文字)は2回参照する必要がありますが、すべてが「危険」ではありませんレベル。結構たくさんあるので、忘れていないかと思います。

3
Fiximan

ファイル名では、'/'はディレクトリ区切り文字であるため禁止されています。それが唯一の理由です。また、ファイルシステムを手動で編集すると、名前に'/'を使用してファイルを作成できる場合もあります(あまり使用できないため、お勧めしません)。

関連するシステムコールはC言語の文字列渡し規則を使用し、NULはそのような文字列のターミネータであるため、NUL文字をファイル名の一部として使用することはできません。したがって、名前の一部として解釈することはできません。

\0というファイルを作成することは、NULを含むファイルを作成することとは異なります。前者は、2つの文字'\'および'0'を含むファイル名です。

2
Toby Speight