ssh / su -cのbashコマンドと引数を書く方法

ssh / su -cのbashコマンドと引数を書く方法

次の単純化されたバージョンのように、ソース配列のリストから項目をバックアップしています。

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つの引数があるとしますrsyncbash -c ...ssh ...printf '%s\n' <args>file1other 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/"

関連情報