4

例を使って質問を説明します...

変数が展開されたときに --chapters の後の単一引用符がエスケープされることを示します (これは予期していませんでした)。

prompt@ubuntu:/my/scripts$ cat test1.sh
#!/bin/bash
actions="--tags all:"
actions+=" --chapters ''"
mkvpropedit "$1" $actions

prompt@ubuntu:/my/scripts$ ./test1.sh some.mkv
Error: Could not open '''' for reading.

そして今、何らかの理由で mkvpropedit はファイル名の一部として二重引用符を受け取ります (これも予期していませんでした):

prompt@ubuntu:/my/scripts$ cat test1x.sh
#!/bin/bash
command="mkvpropedit \"$1\""
command+=" --tags all:"
command+=" --chapters ''"
echo "$command"
$command

prompt@ubuntu:/my/scripts$ ./test1x.sh some.mkv
mkvpropedit "some.mkv" --tags all: --chapters ''
Error: Could not open '''' for reading.

上記のエコーされたコマンドは正しいようです。同じテキストを別のスクリプトに入れると、期待される結果が得られます。

prompt@ubuntu:/my/scripts$ cat test2.sh
#!/bin/bash
mkvpropedit "$1" --tags all: --chapters ''

prompt@ubuntu:/my/scripts$ ./test2.sh some.mkv
The file is being analyzed.
The changes are written to the file.
Done.

引用符が期待どおりに動作しない理由を誰か説明してください。ウェブ上には他にも多くの引用に関する議論があるため、この問題を検索するのは困難であることがわかりました. 例がなければ、質問を説明する方法さえわかりません。

いつの日か、引数のファイル名にすべてを壊す文字が含まれているのではないかと心配しています。スクリプトに直接入力した場合と、変数を介して指定した場合とで、同じコマンドの実行が異なる理由がわかりません。教えてください。

読んでくれてありがとう。

4

1 に答える 1

5

覚えておくべき重要なことは、引用符は、コマンド ラインが最初に解析されるときに一度だけ削除されるということです。$fooパラメータ置換 ( ) またはコマンド置換 ( )の結果としてコマンド ラインに挿入された引用符は$(cmd args)、特殊文字として扱われません。【注1】

それは、空白やグロブのメタ文字とは異なるようです。単語の分割とパス名の展開は、パラメーター/コマンドの置換後に発生します (置換が引用符内で発生しない限り)。【注2】

$argsその結果、次のようなbash 変数を作成することはほとんど不可能です。

cmd $args

引用符が含まれている場合$args、それらは削除されません。内部の単語$argsは、単一の空白文字ではなく、一連の空白文字で区切られます。

これを行う唯一の方法は、$IFS空白以外の文字を含めるように設定することです。$argsその文字は、内部で単一文字の区切り文字として使用できます。ただし、value 内の文字を引用する方法はないため、一度引用すると、選択した文字は区切り記号として以外に使用できなくなります。これは通常、あまり満足のいくものではありません。

ただし、解決策があります。bash 配列です。

配列変数にする場合$argsは、引用符を繰り返す構文で展開できます。

cmd "${args[@]}"

の要素ごとに正確に 1 つの単語を生成し$args、それらの単語の単語分割とパス名の展開を抑制するため、リテラルとして終了します。

たとえば、次のようになります。

actions=(--tags all:)
actions+=(--chapters '')
mkvpropedit "$1" "${actions[@]}"

おそらくあなたが望むことをします。そうでしょう:

args=("$1")
args+=(--tags)
args+=(all:)
args+=(--chapters)
args+=('')
mkvpropedit "${args[@]}"

そしてそうなるだろう

command=(mkvpropedit "$1" --tags all: --chapters '')
"${command[@]}"

それが半透明であることを願っています。

man bash(またはオンライン版)には、「拡張」セクションから始まる、bashがコマンドを組み立てる方法の詳細な説明が含まれています。完全な説明のために読む価値があります。


ノート:

  1. これは、コマンド ライン処理後に引数を再度評価するevalようなコマンドには適用されません。bash -cしかし、これは、コマンド ライン処理が 2 回行われるためです。

  2. 単語分割は、コマンドが解析されるときに発生する「コマンドを単語に分割する」ことと同じではありません。1 つには、単語分割では区切り文字として の値が使用されますが$IFS、コマンド ライン解析では空白が使用されます。ただし、これらはどちらも引用符内で行われないため、その点では似ています。いずれにせよ、単語はパラメータ置換の前後で何らかの方法で分割されます。

于 2013-11-18T00:06:53.063 に答える