回答

回答

问题

我使用find -exec curlubuntu 控制台列出目录中的所有文件并将它们发送到 FTP 服务器:

find * -type f -exec curl -u ***:*** --ftp-pasv --ftp-create-dirs -T {} $PATHDEST/{} \; -exec sleep 2 \;

find * -type f按字母顺序返回文件,我需要先发送最近更改的文件。

可能的解决方案?

我测试了find * -type f -exec ls -1t "{}" +;命令,它按编辑日期对文件进行排序,但我无法将其连接到我的-exec curl

我试过:

find * -type f -exec ls -1t "{}" +; -exec curl -u ***:*** --ftp-pasv --ftp-create-dirs -T {} $PATHDEST/{} \;

但它返回文件列表和错误:

Command '-exec' not found, did you mean:

  command 'kexec' from deb kexec-tools
  command 'jexec' from deb default-jre
  command 'jexec' from deb openjdk-11-jre-headless
  command 'jexec' from deb openjdk-8-jre-headless
  command 'hexec' from deb hexec
  command 'gexec' from deb gexec
  command 'pexec' from deb pexec

Try: sudo apt install <deb name>

问题:

如何执行类似命令

find * ... -exec ls ... -exec curl

答案1

您必须使用“查找”之外的某些东西来对输出进行排序,因此您必须使查找输出能够按日期排序,例如如下所示:

find <search args>. -printf "%T@ %P\0" | sort -rz | awk 'BEGIN {RS="\0";ORS="\0"};{print $2}' | xargs -0 <command>

慢动作:

  • -printf "%T@ %P\0输出由文件时间戳、名称和空分隔符组成的一行
  • sort -rz按时间戳降序排序(最新优先)
  • awk 'BEGIN {RS="\0"};{print $2}'删除时间戳(并保留空分隔符)
  • xargs -0对每个文件执行命令

如果您不希望文件名称中包含空格和换行符,则可以使用更简单的方法:

find <search args>. -printf "%T@ %P\n" | sort -r | awk '{print $2}' | xargs  <command>

既然您提到了 Ubuntu/Bash,我假设您有 GNU find。对于find没有选项的-printf,可以使用-exec stat --format '%y %n'(或-exec stat --printf '%y %n\0'用于空分隔符,但在给定版本中可能find具有stat相同级别的-printf支持)。

答案2

回答

要链接多个-exec命令,您需要在每个命令中转义+;。否则,shell 会在findcan 之前解释它们,这会弄乱您的命令行。

另外,您不能在一个命令中组合+和,因为它们在将结果提供给命令的方式上是互斥的。;-execfind

解释

发生错误是因为您忘记转义第一个 后的分号-exec。手册页告诉我们:

这两种结构可能都需要转义(使用‘\’)或引用,以防止它们被 shell 扩展。

find * -type f \
    -exec ls -1t "{}" +; \ # This is `wrong'
    -exec curl -u ***:*** --ftp-pasv --ftp-create-dirs -T {} $PATHDEST/{} \;

这会导致您的 shell 将其解释为两个命令,第一个命令在第一个分号后结束。由于您的计算机上没有名为“-exec”的命令,因此您会收到错误。

# First command:
find * -type f \
    -exec ls -1t "{}" +;

# Second command -> error! There's no shell command called `-exec'.
-exec curl -u ***:*** --ftp-pasv --ftp-create-dirs -T {} $PATHDEST/{} \;

然而,我认为即使你转义了分号,它也不会起作用,因为你不能组合+;变体。

# Either this
find * -type f -exec ls -1t "{}" \+ -exec etc.

# Or this
find * -type f -exec ls -1t "{}" \; -exec etc.

两者的区别在于它们如何将结果传递给 exec 命令:

-exec 命令;执行命令;[…] 字符串 `{}' 被当前正在处理的文件名替换 […] 对每个匹配的文件运行一次指定的命令。[…]

-exec 命令 {} + -exec 操作的这个变体在选定的文件上运行指定的命令,但命令行是通过在末尾附加每个选定的文件名来构建的;[…]

答案3

谢谢@xenoid!您的解决方案确实帮助了我!

我对你的代码做了一处更改:我把-printf "%T@ %P\0"每个-printf "%T@ %P\0\n"文件名都放在新行中。

这是我修改后的脚本:

find ./ -type f -printf "%T@ %P\0\n" | sort -rz | xargs -I % sh -c 'echo %' | awk '{split($0,a," "); print a[2]}' | xargs -I % sh -c "curl -u ${CREDENTIALS} --ftp-pasv --ftp-create-dirs -T % ${PATHDEST}/%; sleep 2"

我也会慢慢解释一下:

  • find ./ -type f -printf "%T@ %P\0\n"列出目录中的文件,每个文件名占一行
  • | sort -rz按编辑日期对文件进行排序(最新文件排在最上面)
  • | xargs -I % sh -c 'echo %'返回 AWK 的时间戳和文件名
  • | awk '{split($0,a," "); print a[2]}'删除时间戳并仅保留文件名
  • | xargs -I % sh -c "curl -u ${CREDENTIALS} --ftp-pasv --ftp-create-dirs -T % ${PATHDEST}/%使用 xargs 将文件名传递给 CURL-FTP 以将其上传到 FTP 服务器
  • ; sleep 2"仅休眠,因为我的 FTP 服务器在连接过多后返回错误/超时

相关内容