从 -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

你的引用问题来自于你试图解决一个你没有的问题。仅当您处理 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结合使用:-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 来空终止(而不是空白终止)输出,然后 -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}

相关内容