web-dev-qa-db-ja.com

bashスクリプトでスペースをエスケープできないのはなぜですか?

Bashのパスのスペース文字をエスケープしようとしていますが、 バックスラッシュquotes も使用できません。

.shスクリプト:

ROOT="/home/hogar/Documents/files/"
FILE=${ROOT}"bdd.encrypted"
DESTINATION="/home/hogar/Ubuntu\ One/folder"

mv ${FILE} ${DESTINATION}

スクリプトを実行した後(./file)これが結果です:

mv: target 'One/folder' is not a directory

mvコマンドが文字列を分割するのはなぜですか?これを防ぐにはどうすればよいですか?

72
Lucio

DESTINATION変数を拡張しています。echoを実行した場合、これは次のようになります。

echo ${DESTINATION}
/home/hogar/Ubuntu\ One/folder

しかしmvはこれを理解していません:

mv ${FILE} ${DESTINATION}                                                
mv: cannot move '/home/hogar/Documents/files/bdd.encrypted' to '/home/hogar/Ubuntu\\ One/folder': No such file or directory

(何らかの理由で私のmvはより冗長です)

これを防ぐには、代わりに引用符を使用する必要があります。

mv "${FILE}" "${DESTINATION}"

展開する必要がない場合(すでに展開しているため)"$..."で十分です:

mv "$FILE" "$DESTINATION"
84
Braiam

変数を準備するためのステップは「十分に近く」あり、機能しますが、理解の弱さを示しています(そのため、投稿しました!)

DESTINATIONへの割り当ては、次の2つのうちの1つのみである必要があります。

DESTINATION="/home/hogar/Ubuntu One/folder"

または

DESTINATION=/home/hogar/Ubuntu\ One/folder

二重引用符は引用符(何らかの魔法のあるフォーム)をオンにし、バックスラッシュはそれに続く1文字を引用符で囲むことができます。個人的には、二重引用符を使用したフォームの方が視覚的に優れているため、望ましいと思います。

ちなみに、同様の理由で、次のようなFILE割り当てを行います。

FILE="${ROOT}bdd.encrypted"

これは、単に${NAME}の代わりに$NAMEを使用して、変数名を後続の文字から分離するかなりまれな状況を示しています。実際、ROOTの定義に/の末尾がない場合は、

FILE="$ROOT/bdd.encrypted"

それはさらに良く見えます。それはまた、私が入らない時々の利益をあなたに与えるでしょう。私の経験では、末尾のスラッシュは歓迎よりも歓迎されない傾向があり、それらは絶対に必要ではありません。 (シンボリックリンクに関しては影響がありますが、これはすでに大きな回答ですが、詳細が多すぎます)。

問題の核心は、実際にはmvコマンドで発生しています。

mv "$FILE" "$DESTINATION"

最近は、パスを表す変数にスペースが含まれる時期がわからないためです。繰り返しになりますが、単純な変数展開では中括弧を避けていることに注意してください。

問題が発生した理由は、シェルがコマンドラインを構築するために使用するプロセスが原因です。シェルマニュアルを注意深く読んだ場合、基本的に変数(およびその他いくつかのこと)が展開され、その後スペースを探すことになります。もちろん、プロセスはさらに複雑ですが、これが問題の要点です。

関連していて、知っておく価値のあるもう1つのポイントは、$*$@"$*""$@"の違いです。だから話しましょう!

次のように呼び出されるシェルスクリプトがある場合:

foo -x "attached is the software" "please read the accompanying manual"

次に、foo-x$1として、2つの文字列を$2および$3(明らかな理由で"$2"および"$3"としてアクセスする必要があります)として表示します。ただし、このパラメーターセット全体を別のスクリプトに渡したい場合は、次のようにすべてのスペースでひどい分割が発生します。

bar $*

次のコード(非常にオリジナルのBourn Shellのバージョン0.Xでの唯一の方法)は、すべての引数を単一の文字列として渡します(これも間違っています)。

bar "$*"

そのため、次の構文が非常に特殊な魔法のケースとして追加されました。

bar "$@"

$*の個々のメンバーを意図的に引用符で囲んだ完全な文字列として引用しますが、それらは別々に保持します。今は誰もが勝者です。

$@として使用しない場合、"$@"の他の効果を実験するのはちょっと面白いですが、実際には面白いものではないことがすぐにわかります。これは、大きな問題を解決する特別なケースにすぎません。

配列をサポートする適切な最新のシェルはすべて、特殊な場合に@も使用します。

${arr[*]}
"${arr[*]}"
"${arr[@]}"

最初の1つはすべてのスペースで分割され、予測できない数の個別の単語につながります。2つ目はすべての配列メンバーを1つの文字列で生成し、3つ目は各配列メンバーを個々の途切れのない引用文字列として非常にうまく提供します。

楽しい!

27
user56373

はい、値を$DESTINATIONに割り当てるときにスペースをエスケープしました

しかし、mvコマンドで使用すると、

mv ${FILE} ${DESTINATION}

代わりにこれを使用してください:

mv "${FILE}" "${DESTINATION}"
13
daisy