假设我有以下文件和目录结构:
$ tree
.
├── a
├── b
└── dir
└── c
1 directory, 3 files
即两个文件a
和b
以及目录dir
,其他文件c
代表。
我想要使用awk
(GNU Awk 4.1.1
完全)处理所有文件,所以我这样做:
$ gawk '{print FILENAME; nextfile}' * */*
a
b
awk: cmd. line:1: warning: command line argument `dir' is a directory: skipped
dir/c
一切正常,但*
也会扩展到目录dir
,而awk
会尝试处理它。
所以我想知道:有没有本地方式awk
可以检查给定元素是否是文件,如果是,跳过它?也就是说,不使用system()
。
我通过调用BEGINFILE中的外部system
来使其成功:
$ gawk 'BEGINFILE{print FILENAME; if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile}} ENDFILE{print FILENAME, FNR}' * */*
a
a 10
a.wk
a.wk 3
b
b 10
dir
dir is a dir, skipping
dir/c
dir/c 10
还要注意if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile}
直观地解决这个问题:它应该在为true时返回1,但它返回退出代码。
我读了A.5 Extensions in gawk Not in POSIX awk:
- 命令行上的目录会产生警告并被跳过(请参阅Command-line directories)
然后链接的页面说:
4.11命令行上的目录
根据POSIX标准,在awk命令行上命名的文件 必须是文本文件;如果他们不是,这是一个致命的错误。大多数版本 awk将命令行上的目录视为致命错误。
默认情况下,gawk会对命令中的目录生成警告 线,但否则忽略它。这使得shell更容易使用 你的awk程序的通配符:
$ gawk -f whizprog.awk * Directories could kill this program
如果给出了--posix或--traditional选项中的任何一个,那么就是gawk 恢复将命令行上的目录视为致命错误。
请参阅Extension Sample Readdir,了解将目录视为可用的方法 来自awk程序的数据。
实际情况就是这样:与--posix
之前的命令相同失败:
$ gawk --posix 'BEGINFILE{print FILENAME; if (system(" [ ! -d " FILENAME " ]")) {print FILENAME, "is a dir, skipping"; nextfile}} ENDFILE{print FILENAME, NR}' * */*
gawk: cmd. line:1: fatal: cannot open file `dir' for reading (Is a directory)
我查看了上面链接的16.7.6 Reading Directories
部分并讨论了readdir
:
readdir扩展为目录添加了一个输入解析器。用法 如下:
@load" readdir"
但我不确定如何调用它以及如何在命令行中使用它。
答案 0 :(得分:5)
我只是避免将目录传递给awk,因为即使POSIX说所有文件名都必须是文本文件。
您可以使用find
遍历目录:
find PATH -type f -exec awk 'program' {} +
答案 1 :(得分:2)
如果您想保护您的脚本免受其他人错误地将目录(或其他任何不可读的文本文件)传递给它,您可以这样做:
$ ls -F tmp
bar dir/ foo
$ cat tmp/foo
line 1
$ cat tmp/bar
line 1
line 2
$ cat tmp/dir
cat: tmp/dir: Is a directory
$ cat tst.awk
BEGIN {
for (i=1;i<ARGC;i++) {
if ( (getline line < ARGV[i]) <= 0 ) {
print "Skipping:", ARGV[i], ERRNO
delete ARGV[i]
}
close(ARGV[i])
}
}
{ print FILENAME, $0 }
$ awk -f tst.awk tmp/*
Skipping: tmp/dir Is a directory
tmp/bar line 1
tmp/bar line 2
tmp/foo line 1
$ awk --posix -f tst.awk tmp/*
Skipping: tmp/dir
tmp/bar line 1
tmp/bar line 2
tmp/foo line 1
每个POSIX getline
返回-1
如果/当它尝试从文件中检索记录失败时(例如,不可读的文件或文件不存在或文件是目录),您只需要GNU awk如果你关心,可以通过ERRNO
的价值告诉你哪些失败。