我有一个 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
你的引用问题来自于你试图解决一个你没有的问题。仅当您处理 shell 时才需要引用参数,并且如果直接find
调用rsync
,则不涉及 shell。使用视觉输出并不是判断它是否有效的好方法,因为您无法看到每个参数的开始和结束位置。
这就是我的意思:
# 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
每个匹配的文件。有两种方法可以解决这个问题。
正如其他人指出的那样,您可以使用管道将文件列表传输到rsync
标准输入:
# find . -type f -print0 | rsync --files-from=- -0 . dest/
# ls dest/
foo'"bar
这将使用空字节作为文件名分隔符,因为文件名称中不能包含空字节。
如果您使用 GNU find
,您还有另一种调用方法-exec
,那就是-exec {} +
.在这种风格中,find
一次会传递多个参数。但是,所有参数都添加到命令的末尾,而不是中间。您可以通过一个小 shell 传递参数来解决这个问题:
# find . -type f -exec sh -c 'rsync "$@" dest/' {} +
# ls dest/
foo'"bar
这会将文件列表传递给,sh
然后将它们替换为"$@"
答案2
与find
结合使用:-print0
rsync --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
-print0
rsync
rsync
find $PATH [your conditions here] -print > my-file-list.txt
rsync --files-from=my-file-list.txt $PATH $DESTINATION
当然,由于我们使用了-print0
and 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 来空终止(而不是空白终止)输出,然后 -0在 xargs 上告诉它期待这种输入...这是为了避免破坏嵌入的空白。
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}