-execを使用して、文字列から不明な文字をエスケープします。

-execを使用して、文字列から不明な文字をエスケープします。

特定のファイルとディレクトリを検索する find コマンドがあります。その後、 find コマンドは、以前に見つかったファイルとディレクトリをソースとして使用して rsync を実行します。問題は、これらのファイルやディレクトリにWindowsなどの違法な文字だけでなく、単一引用符、二重引用符など、さまざまな文字を含めることができることです。

rsyncまたは他のコマンドで使用するために文字列を動的にエスケープするにはどうすればよいですか?

このコマンドは、rsyncソース文字列に二重引用符をハードコーディングすることで機能しますが、文字列に二重引用符が含まれていると中断されます。

find "/mnt/downloads/cache/" -depth -mindepth 1 \( \
-type f \! -exec fuser -s '{}' \; -o \
-type d \! -empty \) \
\( -exec echo rsync -i -dIWRpEAXogt --numeric-ids --inplace --dry-run '"{}"' "${POOL}" \; \)

結果出力:

rsync -i -dIWRpEAXogt --numeric-ids --inplace --dry-run "test/this " is an issue" /mnt/backing

回答の情報を適用した後の作業コマンド:

find "/mnt/downloads/cache/" -depth -mindepth 1 \( \
                             -type f \! -exec fuser -s {} \; -o \
                             -type d \! -empty \) \
                             \( -exec rsync -i -dIWRpEAXogt --remove-source-files-- "${POOL} \; \) \
                             -o \( -type d -empty -exec rm -d {} \; \)

答え1

あなたの引用の問題は、あなたが持っていない問題を解決しようとするために発生します。パラメータ引用符はシェルを扱う場合にのみ必要であり、直接find呼び出すとrsyncシェルは関連しません。ビジュアル出力を使用することは、各パラメータの始まりと終わりを確認できないため、動作するかどうかを確認するための良い方法ではありません。

これが私が意味するものです:

# touch "foo'\"bar"

# ls
foo'"bar

# find . -type f -exec stat {} \;
  File: ‘./foo'"bar’
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: fd00h/64768d    Inode: 1659137     Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1004/ phemmer)   Gid: ( 1004/ phemmer)
Access: 2017-12-09 13:21:28.742597483 -0500
Modify: 2017-12-09 13:21:28.742597483 -0500
Change: 2017-12-09 13:21:28.742597483 -0500
 Birth: -

{}argでは引用しませんでしたstat

ただし、rsync一致するすべてのファイルを呼び出すため、コマンドのパフォーマンスが非常に低下します。この問題を解決する方法は2つあります。

他の人が指摘したように、ファイルのリストをrsync標準入力にパイプすることができます。

# find . -type f -print0 | rsync --files-from=- -0 . dest/

# ls dest/
foo'"bar

ファイル名にヌルバイトを含めることはできないため、ファイル名区切り文字としてヌルバイトを使用します。

 

GNUを使用している場合をfind呼び出す別の方法があります。-execつまり、-exec {} +このスタイルではfind複数の引数が一度に渡されます。ただし、すべてのパラメータはコマンドの途中ではなくコマンドの最後に追加されます。小さなシェルを介してパラメータを渡すと、この問題を解決できます。

# find . -type f -exec sh -c 'rsync "$@" dest/' {} +

# ls dest/
foo'"bar

これにより、ファイルのリストが渡され、sh次に置き換えられます。"$@"

答え2

以下と組み合わせて使用​​してくださいfind-print0rsync --files-from=- -0

find "${Share}" -depth -mindepth 1 \( \
-type f \! -exec fuser -s '{}' \; -o \
-type d \! -empty \) -print0 | rsync --files-from=- -0 "${Share}" ${POOL}

findこれで(〜のために)区切りの見つかったファイルのリストが作成されます。 NULはファイル名に有効な文字ではないため、リスト内の区切り文字として安全に使用できます。リストは標準出力として印刷され、読み取られます。通常、ソースファイルを引数として使用しますが、ファイルを読み取ることもできます。\0-print0rsyncrsync

find $PATH [your conditions here] -print > my-file-list.txt
rsync --files-from=my-file-list.txt $PATH $DESTINATION

もちろん、-print0and notを使用したので、ファイルが次のように区切られていること-printを知らなければなりません。rsync\0

find $PATH [your conditions here] -print0 > my-file-list.txt
rsync --files-from=my-file-list.txt -0 $PATH $DESTINATION

-引数を使用して、標準入力からファイルを読み取る必要があることを--files-from知らせ、中間ファイルを削除できます。rsync

find $PATH [your conditions here] -print0 \
  | rsync --files-from=- -0 $PATH $DESTINATION

パスはrsyncまだソースとして必要ですが、転送のみ可能ですfind

答え3

この場合、正確な引用は非常に困難です。私は通常あきらめて他のツールを試してみます。申し訳ありません。これはテストされておらず、試みる入力がないため、深刻に壊れる可能性があると推測されます。 :) find で print0 を使用して出力を null で終了し(空白の終了ではなく)、xargs で -0 を使用します。この種の入力を期待しています...これは、埋め込まれた空白が壊れるのを防ぐためです。

find "${Share}" -depth -mindepth 1 \( \
-type f \! -exec fuser -s '{}' \; -o \
-type d \! -empty \) -print0 | \
xargs -0 -I{} rsync -i -dIWRpEAXogt --numeric-ids --inplace --dry-run {} ${POOL}

関連情報