问题
我使用find -exec curl
ubuntu 控制台列出目录中的所有文件并将它们发送到 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 会在find
can 之前解释它们,这会弄乱您的命令行。
另外,您不能在一个命令中组合+
和,因为它们在将结果提供给命令的方式上是互斥的。;
-exec
find
解释
发生错误是因为您忘记转义第一个 后的分号-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 服务器在连接过多后返回错误/超时