如何使用 bash 检查目录中的所有文件以查看哪些文件(如果有)被写入了内容?

abr*_*bra 5 grep bash

我运行了一个作用于多个“人”的脚本,并为每个人创建输出和错误文件。让我们这样说:

output_alice.txt
error_alice.txt
output_bob.txt
error_bob.txt
.
.
.

Run Code Online (Sandbox Code Playgroud)

我想要一个命令来扫描所有错误文件 ( error_<name>.txt) 并回显已写入内容的文件(而不是空),作为识别脚本因错误而退出的“人”的快速方法。是否有捷径可寻?我知道如何使用 grep 对字符串执行此操作,例如grep -r <substring> .,但不知道如何检查是否有任何内容。

Sté*_*las 15

请注意,bash 不是终端,它是众多shell之一,它们是某些专门运行命令的编程语言的解释器。与大多数应用程序一样,它可以将其输入/输出连接到终端设备或任何其他类型的文件。

要以 bash 和大多数其他 Unix shell 语言列出当前工作目录中命名的至少包含一行的l文件,您可以执行以下操作:error_anything.txt

grep -l '^' error_*.txt
Run Code Online (Sandbox Code Playgroud)

其中^是在主题开头匹配的正则表达式,主题grep.

对于至少有一个非空文本行的人:

grep -l . error_*.txt
Run Code Online (Sandbox Code Playgroud)

哪里.匹配任何单个字符。请注意,对于使用不同于区域设置的字符映射编码的文件,如果其内容无法解码为文本,则可能无法匹配非空行。

另请注意,并非所有grep实现都会报告仅包含一个未终止行的文件(其中一个缺少行分隔符,如 的输出中所示printf invalid-text-as-missing-the-last-newline)。

另一种方法是查找至少包含一个字节的文件:

find -L . ! -name . -prune -name 'error_*.txt' -type f -size +0c
Run Code Online (Sandbox Code Playgroud)

这还有一个好处是可以忽略非常规类型的文件例如目录、套接字......)

或者使用 zsh shell:

print -rC1 -- error_*.txt(N-.L+0)
Run Code Online (Sandbox Code Playgroud)

对于符号链接,-考虑-L其目标的大小和类型,.其行为相当于-type f和(对于ullglobL+0来说,如果没有匹配的文件,则不会报告错误)。-size +0cNN

这样做的好处是不包含./前缀,即使用户名无法在区域设置中解码为文本,也可以工作,并且可以为您提供一个(默认情况下按词法)排序的列表。

r您可以将其扩展为仅打印用户名(第一个之后的文件 oot 名称的部分_):

{}{ print -rC1 -- ${@#*_}; } error_*.txt(N-.L+0:r)
Run Code Online (Sandbox Code Playgroud)

要列出error自运行命令以来已修改的文件,您可以使用谓词-newerof并与在运行命令之前find已编辑的文件进行比较:touch

touch .before
my-command-that-may-write-to-error-files
find -L . ! -name . -prune -name 'error_*.txt' -type f -size +0c -newer .before
Run Code Online (Sandbox Code Playgroud)

在 zsh 中,您可以将find命令替换为:

print -rC1 -- error_*.txt(N-.L+0e['[[ $REPLY -nt .before ]]'])
Run Code Online (Sandbox Code Playgroud)

对于某些find实现,您可以替换! -name . -prune-mindepth 1 -maxdepth 1,但-maxdepth 1也可以在这里工作,因为深度 0 ( ) 的文件与.其他条件不匹配(它既不匹配也不匹配-name 'error_*.txt'-type f

date通过and的 GNU 实现find(这也是find引入-maxdepth谓词的实现),您可以通过执行以下操作来避免创建该.before文件:

before=$(date +'@%s.%N')
my-command-that-may-write-to-error-files
find -L . -maxdepth 1 -name 'error_*.txt' -type f -size +0c -newermt "$before"
Run Code Online (Sandbox Code Playgroud)

使用 时zsh,您可以将 替换before=$(date +'@%s.%N')print -Pv before '@%D{%s.%N}'before=${(%):-@%{%s.%N}D}before=@$EPOCHREALTIME(在 之后zmodload zsh/datetime);find您可以再次使用glob qualifiers来避免调用,甚至可以再次使用匿名函数来避免临时变量,但这会变得非常复杂:

zmodload zsh/stat
zmodload zsh/datetime
() {
  my-command-that-may-write-to-error-files
  print -rC1 error_*.txt(N-.L+0e['
    stat -F %s.%N -A2 +mtime -- $REPLY && (( $2 > $1 )) '])
} $EPOCHREALTIME
Run Code Online (Sandbox Code Playgroud)

请注意,至少在 Linux 上,尽管系统和文件系统支持纳秒精度,但粒度要小得多。您甚至可以发现修改时间是在修改到初始调用date或引用之前的某个值时设置的$EPOCHREALTIME,因此这些方法可能不适用于运行时间不到一厘秒的命令。删除Nanoseconds 并替换>>=-newerwith ! -older(如果您的find实现支持它,但不太可能)可能是更好的方法。


Fel*_*xJN 10

GNUfind提供了非 POSIX 选项来列出空文件,只需否定该测试:

find /path/to/dir -type f -name 'error_*.txt' ! -empty
Run Code Online (Sandbox Code Playgroud)

如果不在子目录中搜索,请-maxdepth 1在路径后添加。

在 POSIX 中find检查文件大小是0可行的:

find /path/to/dir -type f -name 'error_*.txt' ! -size 0
Run Code Online (Sandbox Code Playgroud)