Bashスクリプトでスペースをエスケープできないのはなぜですか? [コピー]

Bashスクリプトでスペースをエスケープできないのはなぜですか? [コピー]

Bashでパスの空白文字をエスケープしようとしましたが、どちらも機能しませんでした。バックスラッシュまたは引用符働く

.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コマンドが文字列を分割するのはなぜですか。これが起こらないようにするにはどうすればよいですか?

答え1

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"

答え2

変数を準備する手順は「十分に近い」と動作しますが、理解が不足していることを示しています(それで投稿しました!)

DESTINATIONの割り当ては、次のいずれかが必要です。

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

または

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

二重引用符は引用符を開き(魔法の一形態)、バックスラッシュはその後の単一文字を引用します。個人的に二重引用符を使用した形式が視覚的に良いので、より良いと思います。

しかし、同様の理由で、次のようにFILE割り当てを実行します。

FILE="${ROOT}bdd.encrypted"

${NAME}$NAMEこれは、単に変数名とそれに続く文字を区別するために-を使用するかなりまれなケースを示しています。実際にROOTが末尾/なしで定義されている場合は、次のように作成します。

FILE="$ROOT/bdd.encrypted"

よさそうだねまた、私の経験では言及していないいくつかの利点も時々提供しています。末尾のスラッシュは人気よりも人気がない傾向があり、確かに必要ありません。 (シンボリックリンクには影響しますが、すでに大きな回答に比べて詳細すぎます。)

問題の鍵は、実際には次のような場合に発生します。MVコマンドは次のように書く必要があります。

mv "$FILE" "$DESTINATION"

これは、パスを表す変数にいつ空白があるのか​​わからないためです。単純な変数拡張には括弧を使用しないように注意してください。

問題が発生する理由は、シェルがコマンドラインを作成するために使用するプロセスによるものです。シェルのマニュアルを注意深く読んでみると、基本的に変数(およびその他のいくつかの点)が拡張されてからスペースを見つけることがわかります。もちろん、プロセスはより複雑ですが、これが質問の中心です。

関連して知っている価値があるもう一つのこと:$*$@"$*"の違いです"$@"。だから私は教えてください!

シェルスクリプトがある場合は、次のように呼び出すことができます。

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

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

bar $*

次のもの(元のBournシェル0.Xバージョンの唯一の方法)は、すべての引数が単一の文字列として渡されるようにします(やはり間違っています)。

bar "$*"

したがって、次の構文は非常に特別なケースとして追加されました。

bar "$@"

意図的に、個々のメンバーを$*完全な引用文字列として引用しますが、区切ります。今誰もが勝者です。

$@使用しない場合は、他の効果を試すのは少し面白いですが、"$@"実際にはそれほど面白くないことがすぐにわかります。これは主な問題を解決する特別なケースです。

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

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

1つ目はすべての空白に分割され、予測不能な数の個々の単語が生成され、2つ目はすべての配列メンバーを1つの文字列として生成し、3つ目は各配列メンバーを別々の完全な見積もり文字列として見事に提供します。

楽しむ!

答え3

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

しかし、mvコマンドと一緒に使うとそうではありません。

mv ${FILE} ${DESTINATION}

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

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

関連情報