答案1
测试单个文件
grep
如果以下命令some_file
至少包含一个空字符,则返回退出状态 0:
<some_file tr -dc '\0' | tr '\0' '\n' | grep -q ''
除非设置了 shell 选项,否则,如果s exit.默认未设置,而您希望这样,则pipefail
的退出状态grep
将成为整个管道的退出状态(请参阅tr
pipefail
否则会发生什么)。
我写“如果tr
s 退出”,是因为grep
退出后第二个tr
需要写入一些内容才能获得 SIGPIPE;然后第一个tr
需要写入一些内容才能获得 SIGPIPE;只有这样管道才被视为终止。tr
即使grep
提前退出并且结果已知,第一个也可能会继续读取。如果some_file
是一个生成永无止境的字节流的特殊文件(类似于/dev/urandom
),并且流中没有足够的空字节,则管道将永远不会退出。对于常规文件,最坏的情况是第一个在tr
读取整个文件后退出。如果some_file
是一个常规文件,那么tr
s 将退出最终一定。
我的这个答案解释了一个可以加快速度的技巧。对于你的情况,这个技巧会将tr
(s) 留在后台。由于你要测试许多文件,因此堆积tr
s 不是一个好主意。
实际上,测试文件的最开头通常就足够了。以下命令将读取最多 2 KiB 的内容some_file
并仅分析此部分:
head -c 2048 some_file | tr -dc '\0' | tr '\0' '\n' | grep -q ''
或者,您可以使用命令file
,对于大文件,它也不会读取整个文件。如果file --mime-type
不打印,我们在这里生成退出状态 0 text/whatever
:
! file --brief --mime-type some_file | grep -q 'text/'
我希望这两个命令在绝大多数情况下能够一致;但在某些情况下(文件)它们可能会有所不同。
测试多个文件(并进行相应移动)
此代码片段将循环遍历当前工作目录中的文件,测试常规文件并相应地移动它们:
#!/bin/bash
(
shopt -s nullglob
for f in ./*; do
[ -f "$f" ] \
&& ! [ -L "$f" ] \
&& head -c 2048 "$f" | tr -dc '\0' | tr '\0' '\n' | grep -q '' \
&& mv -v "$f" /target/directory/
done
)
笔记:
预先創建
/target/directory/
。您可以使用其他测试。相关行将是:
&& ! file --brief --mime-type "$f" | grep -q 'text/' \
子 shell
(…)
适用于您想要将代码粘贴到交互式 shell 的情况。有了子 shell,代码不会更改当前 shell 中的任何内容。通常
*
不匹配隐藏文件。附加dotglob
到该shopt -s
行以*
匹配隐藏文件。如果您想要递归,请附加
globstar
到shopt -s
行并使用./**
而不是./*
。请注意,如果有同名文件,则可能会丢失数据;请考虑mv -i
。我们想要有条件地移动常规文件。
[ -f "$f" ]
检查我们处理的是否是常规文件;但对于指向常规文件的符号链接(指向常规文件的符号链接(指向常规文件的符号链接(…))),它也会成功。这就是我们额外检查文件是否不是符号链接的原因( )。如果您希望代码将指向常规文件的符号链接视为常规文件,请删除包含(包括终止换行符)! [ -L "$f" ]
的整行。[ -L
一般而言,此答案中的命令和代码不可移植。您标记了狂欢并说操作系统是 Ubuntu,我利用了 Bash 和 Ubuntu 提供的功能。
解决方案
find
是可能的。我们的每个测试都是一个管道,因此find
无论如何都必须生成 shell。