find
命令可以将文件名输出为空分隔字符串(如果-print0
提供),并且可以在打开选项的xargs
情况下使用它们。-0
但在这之间,很难操纵该文件集合 -sort
命令有-z
开关,这使得可以对这些文件进行排序,但又head
没有tail
它们。
我怎样才能以方便的方式对这些空分隔的输入进行head
and操作? tail
(我总是可以创建一个简短而缓慢的 ruby 脚本,但我希望有更好的方法)
答案1
GNUhead
以及tail
自 coreutils 8.25 版本以来有一个-z
选项。
对于旧版本或非 GNU 系统,您可以尝试交换\0
和\n
:
find ... -print0 |
tr '\0\n' '\n\0' |
head |
tr '\0\n' '\n\0'
请注意,某些head
实现无法处理 NUL 字符(POSIX 并不要求它们这样做),但 find 支持-print0
,head
并且文本实用程序通常支持 NUL 字符。
您还可以使用函数将任何命令包装在两个tr
s 之间:
nul_terminated() {
tr '\0\n' '\n\0' | "$@" | tr '\0\n' '\n\0'
}
find ... -print0 | nul_terminated tail -n 12 | xargs -r0 ...
请记住,在 下nul_terminated
,a\0
表示换行符。例如,替换\n
为_
:
find . -depth -name $'*\n*' -print0 | nul_terminated sed '
p;h;s,.*/,,;s/\x0/_/g;H;g;s,[^/]*\n,,' | xargs -r0n2 mv
(\x0
也是 GNU 扩展)。
如果您需要运行多个过滤命令,你可以这样做:
find ... -print0 |
nul_terminated cmd1 |
nul_terminated cmd2 | xargs -r0 ...
但这意味着运行一些冗余tr
命令。或者,您可以运行:
find ... -print0 | nul_terminated eval 'cmd1 | cmd2' | xargs -r0 ...
答案2
在我的旧系统上,支持空分隔输入,所以我这样做作为获取 3 个最新文件grep
的替代:head --zero-terminated -n 3
find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 ""
sort
和的组合grep
将允许替换 以tail
获得第三个最新文件:
find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | grep -zm 1 ""
仅文件名:
find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | grep -zPom 1 "(?s)/.*"
遗憾的是-o
(仅匹配)禁用了零分隔输出(bug!?),该输出是通过启用的-z
并返回带有结束新行的文件名,这不允许进一步的管道
find . -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | grep -zPom 1 "(?s)/.*" | xargs -0 ls -l
ls: cannot access /te
st
: No such file or directory
这可以通过使用来解决read
:
find . -mindepth 1 -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zrn | grep -zm 3 "" | sort -zn | { IFS=" " read -rd '' mtime file && echo -en "$file\0"; } | xargs -0 -n1 ls -l
-rw-r--r-- 1 root root 0 Apr 24 17:46 ./te?st
(ls -t
按预期返回文件名中新行字符的问号)
或者另一个例子,如何获取当前目录中最旧的 100 个文件:
find /tmp/test -mindepth 1 -maxdepth 1 -type f -printf "%T@ %p\0" | sort -zn | grep -zm 10 "" | { while IFS=" " read -rd '' mtime file; do echo -en "$file\0"; done; } | xargs -0 -n1 ls -bl
-rw-r--r-- 1 root root 0 Mar 23 14:32 /tmp/test/foo\ baz.txt
-rw-r--r-- 1 root root 0 Apr 26 10:43 /tmp/test/foo\nbar\ baz.txt
...