如何在 bash 中对空分隔输入执行“head”和“tail”?

如何在 bash 中对空分隔输入执行“head”和“tail”?

find命令可以将文件名输出为空分隔字符串(如果-print0提供),并且可以在打开选项的xargs情况下使用它们。-0但在这之间,很难操纵该文件集合 -sort命令有-z开关,这使得可以对这些文件进行排序,但又head没有tail它们。

我怎样才能以方便的方式对这些空分隔的输入进行headand操作? 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 字符。

您还可以使用函数将任何命令包装在两个trs 之间:

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

相关内容