web-dev-qa-db-ja.com

評価、変数、引用符に関するBashの問題

私はここや他のどこでもbashの引用について読んでいますが、この問題を解決する助けがありませんでした。

問題は、ループでバックアップを実行するための小さなスクリプトがあるということです。

evalを使用しない場合、rsync$OPTIONS変数に問題があります。

しかし、私がevalを使用する場合、問題は変数$CURRENT_DIR ..に行きます。

rsyncは次のメッセージを返します: '予期しないローカル引数:/ path/with'

変数$CURRENT_DIRを引用するあらゆる方法を試しました

CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS="-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete"
eval rsync --delete-excluded -i $OPTIONS  [email protected]$f $CURRENT_DIR/xxx/$DIR/files

スペースによる問題なしに変数$CURRENT_DIRを使用できる方法はありますか?

14
Cesar
eval rsync --delete-excluded -i $OPTIONS  [email protected]$f "\"$CURRENT_DIR/xxx/$DIR/files\""

command "some thing"は、1つの引数some thingでコマンドを実行します。引用符はシェルによって解析され、コマンドの実行時に引数が配列として設定されます。コマンドは、引数を引用符なしで何かと見なします。

Evalコマンドは、引数をシェルに入力されたかのように多かれ少なかれ扱います。したがって、eval command "some thing"の場合、bashはevalcommandsome thingの2つの引数で実行します(ここでも、bashが引数の配列を設定するときに引用符が使用されます)。したがって、evalは、シェルにcommand some thingと入力したかのように機能しますが、これは必要なことではありません。

私がしたことは、単に引用符をエスケープして、bashが文字通り"something"引用符をevalに含めるようにすることでした。 evalは、command "some thing"を入力したかのように機能します。

私のコマンドの外側の引用符は厳密には必須ではなく、単なる習慣です。あなたも同様に使用することができます:

eval rsync --delete-excluded -i $OPTIONS  [email protected]$f \"$CURRENT_DIR/xxx/$DIR/files\"
16
Erik

Evalの使用は危険であり、可能な限り避ける必要があります。この場合の主な問題は、OPTIONSを複数の単語を含むものとして定義しようとしていることであり、bash変数はこれをうまく処理しません。解決策があります。単純な変数ではなく、配列にOPTIONSを配置します(スペースがWordの区切り文字として扱われないように、すべての変数参照を二重引用符で囲みます)。

CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS=(-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete)
rsync --delete-excluded -i "${OPTIONS[@]}"  "[email protected]$f" "$CURRENT_DIR/xxx/$DIR/files"
7
Gordon Davisson

ゴードンに同意します

この場合、evalは必要ありません(変数から変数名を作成したり、その場で式を作成したりすることはありません)。

そして、保存したいスペースがある可能性のあるすべての変数参照を二重引用符で囲みたい

しかし、もう1つの良い習慣は、常に{} ..で変数を参照することです。

  "${CURRENT_DIR}" 

の代わりに

  $CURRENT_DIR

これにより、名前のあいまいさがなくなります

3
nhed

一般的な再利用可能なソリューション

物事を正しく引用する方法を理解することは重要ですが、使いやすさとスリップアップを防ぐために、私は次の関数を使用することを好みます。

以下は、配列の各要素を引用することにより、引数にスペースを保持します。

_function token_quote {
  local quoted=()
  for token; do
    quoted+=( "$(printf '%q' "$token")" )
  done
  printf '%s\n' "${quoted[*]}"
}
_

使用例:

_$ token_quote token 'single token' token
token single\ token token
_

上記では、_single token_のスペースが_\_として引用されていることに注意してください。

_$ set $(token_quote token 'single token' token)
$ eval printf '%s\\n' "$@"
token
single token
token
$
_

これは、トークンが実際に別々に保持されていることを示しています。


信頼できないユーザー入力がある場合:

_% input="Trying to hack you; date"
_

評価するコマンドを作成します。

_% cmd=(echo "User gave:" "$input")
_

一見正しい引用でそれを評価します:

_% eval "$(echo "${cmd[@]}")"
User gave: Trying to hack you
Thu Sep 27 20:41:31 +07 2018
_

ハッキングされたことに注意してください。 dateは、文字通りに印刷されるのではなく実行されました。

代わりにtoken_quote()を使用:

_% eval "$(token_quote "${cmd[@]}")"
User gave: Trying to hack you; date
%
_

evalは悪ではありません-誤解されているだけです:)

1
Tom Hale

おそらくすでに使用していると思いますが、一重引用符はどうですか? (このタイプ '')?

0
7dr3am7