次の単純化されたバージョンのように、ソース配列のリストから項目をバックアップしています。
sources=( file1 file2 "other stuff" )
backupcommand="rsync -ar ${a[@]} dest/"
su backupuser -c "$backupcommand"
$backupcommand
正しいエスケープを使用すると問題が発生します。上記の意味は、rsyncが「other stuff」というファイルではなく、「other」というファイルと「stuff」という別のファイルを見つけることです。
引用したコマンドsh -c ...
(またはssh ...
何でも)をどのように設定できますか?バッシュを使っています。
答え1
まず、bash配列の内容を表示する方法が必要ですsources
。良いアプローチprintf '%s\n' <args>
は、.NETの各引数に対して1行を出力することを使用するようです<args>
。
たとえば、
$ sources=( file1 file2 "other stuff" )
$ printf '%s' "${sources[@]}"
file1
file2
other stuff
これをコマンドとして使用すると、バックアッププログラム(たとえば)が実際に引数として受け取っているものをbackupcommand
確認できます。したがって、(or)を介して実行したいコマンドがあり、2つの引数があるとしますrsync
。bash -c ...
ssh ...
printf '%s\n' <args>
file1
other stuff
ターゲットシェルでこのコマンドを実行しようとしています。
$ printf '%s\n' file1 other\ stuff
file1
other stuff
したがって、これを議論として活用するには引用が必要です。私たちは引用するために""
or''
を使用することができます\
。printf '%s\n'
この操作が正常に行われたことを確認するために再利用できます。
$ # With backslashes:
$ printf '%s\n' printf\ \'%s\\n\'\ file1\ other\\\ stuff
printf '%s\n' file1 other\ stuff
$ # With single quotes
$ printf '%s\n' 'printf '\''%s\n'\'' file1 other\ stuff'
printf '%s\n' file1 other\ stuff
$ # Using a variable to store the command...
$ the_command='printf '\''%s\n'\'' file1 other\ stuff'
$ # ..then using that variable as a single argument...
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
これまではとてもよかったです。以前のバージョンでは、コマンドを含む変数を参照していました。
これで配列をパラメータとして使用できますか?私たちがしなければならないのは、配列内の各項目を引用符/エスケープして単一の文字列に追加することですthe_command
。
${var[@]}
各配列項目に対してトークンを生成するように bash に指示します。しかし、これはすでに解釈されているためエスケープされません。したがって、これをシェルコマンド文字列に入れることはできません。したがって、参照を適用する方法が必要です。
Bashはprintf '%q' arg
引用する必要がありますarg
。コマンド置換を使用できます。$(...)
$ sources=( file1 other\ stuff )
$ the_command=printf\ \'%s\\n\'
$ for item in "${sources[@]}"
do the_command="$the_command "$(printf '%q' "$item")
done
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
長く生きる!ただし、次のように整理できます。
$ sources=( file1 other\ stuff )
$ the_command="printf '%s\\n'"$(printf ' %q' "${sources[@]}")
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
$ # And as final proof...
$ bash -c "$the_command"
file1
other stuff
元の質問に適用されます。
sources=( file1 file2 "other stuff" )
backupcommand="rsync -ar "$(printf ' %q' "${sources[@]}")" dest/"
su backupuser -c "$backupcommand"
一般化する
printf '%q' arg
引用符arg
- コマンド置換は、
$(...)
コマンド出力に基づいて単一のトークンを生成します。 - さまざまな引用/エスケープ方法が利用可能です。最も読みやすい方法を選択してください。
答え2
次のように、配列の周りにエスケープされた引用符を追加します。
backupcommand="rsync -ar \"${a[@]}\" dest/"