動的rsyncコマンドで二重引用符をエスケープします。

動的rsyncコマンドで二重引用符をエスケープします。

現在、ユーザーのホームディレクトリをランダムに選択したディレクトリにコピーし、rsyncを使用して同じディレクトリ(存在する場合)から最後に作成されたコピーに接続するスクリプトを作成しています。

コマンドは次のように構築され実行されます。

...
[ -z $LAST_SNAPSHOT ] && LINK_DEST="" || LINK_DEST="--link-dest \"$BACKUP_ROOT/$LAST_SNAPSHOT\""
...

/usr/bin/rsync $OPTS $EXCLUDES $LINK_DEST "$MASTER/" "$NEW_SNAPSHOT"

rsyncスクリプトを実行すると、次のエラーが発生します。

--link-dest arg does not exist: "/home/backuptest/dest/backuptest/20180619_134044"

これは、ディレクトリが実際には存在しないが存在する場合に予想されます。

変数を生成するコードから引用符を削除すると、$LINK_DEST次のコードが生成されます。

[ -z $LAST_SNAPSHOT ] && LINK_DEST="" || LINK_DEST="--link-dest $BACKUP_ROOT/$LAST_SNAPSHOT"

これでrsyncは文句を言わない。パスにスペースがないようにしたいので、二重引用符が必要です。

エラーが何であるかを理解できません。 rsyncを呼び出す方法ですか、それともシェルを誤解していますか?

答え1

エスケープされた二重引用符は、ファイル名のリテラル文字として扱われます。を使用しているので、bash配列を使用してそれを処理できます。

linkDest=()
[[ -n "$LAST_SNAPSHOT" ]] && linkDest+=('--link-dest' "$BACKUP_ROOT/$LAST_SNAPSHOT")
...

rsync $OPTS $EXCLUDES "${linkDest[@]}" "$MASTER/" "$NEW_SNAPSHOT"

"${linkDest[@]}"参照が空の場合、参照は完全に消えます。それ以外の場合は、その値の参照リストに展開されます。$OPTSとがリストであれば、$EXCLUDES同じメカニズムを使用します。

答え2

問題は、ファイル名の一部として二重引用符があることです。

オプションを保存するには、配列を使用しますrsync

シェルから/bin/sh(シェルでも動作可能bash):

# options that are always set
set -- --archive --verbose

# add --link-dest=DIR if needed
if [ -n "$LAST_SNAPSHOT" ]; then
    set -- "$@" --link-dest="$BACKUP_ROOT/$LAST_SNAPSHOT"
fi

# add some --excludes
set -- "$@" --exclude='*.ext' --exclude='dir/***'

# call rsync
rsync "$@" "$MASTER/" "$NEW_SNAPSHOT"

bashこれは、位置引数配列であるPOSIXシェルで使用できる唯一の配列(拡張、類似などでない場合)を利用します。この配列の項目を設定して(を使用してset)、必要に応じて追加することで、引用符付き引数rsync(またはすべてのコマンド)が正しく処理されるようにすることができます。呼び出し内の配列内の項目が二重引用符で個別に囲まれていることをrsync確認"$@"して、シェルがファイル名をトークン化して生成するのを防ぎます。

同等ですがbash配列を使用する場合:

# options that are always set
opts=( --archive --verbose )

# add --link-dest=DIR if needed
if [ -n "$LAST_SNAPSHOT" ]; then
    opts+=( --link-dest="$BACKUP_ROOT/$LAST_SNAPSHOT" )
fi

# add some --excludes
opts+=( --exclude='*.ext' --exclude='dir/***' )

# call rsync
rsync "${opts[@]}" "$MASTER/" "$NEW_SNAPSHOT"

答え3

違いは、エスケープ引用符が\"それを文字列文字に変換することです。

したがって、エラーが正しい可能性があります。

"/ホーム/バックアップテスト/対象/バックアップテスト/20180619_134044"

名前に引用符が含まれる部分は次のとおりです。

/ホーム/バックアップテスト/ターゲット/バックアップテスト/20180619_134044

これがあなたが望むものです。この問題を解決するための推奨される方法は、${}変数を使用することです。たとえば、次のようになります。 $ myvar=var $ echo "\"$myvar\"" "var" $ echo "${myvar}" var

したがって、 "--link-dest "${BACKUP_ROOT}"/"${LAST_SNAPSHOT}"" のようなコマンドを使用すると、より近づきます。

このコンテンツに関する追加情報: https://stackoverflow.com/questions/8748831/when-do-we-need-curly-braces-around-shell-variables そして http://wiki.bash-hackers.org/syntax/pe#simple_usage

関連情報