如何跳过awk中的目录?

时间:2015-12-01 10:27:19

标签: awk gawk dir

假设我有以下文件和目录结构:

$ tree
.
├── a
├── b
└── dir
    └── c

1 directory, 3 files

即两个文件ab以及目录dir,其他文件c代表。

我想要使用awkGNU 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

  

然后链接的页面说:

  

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"

但我不确定如何调用它以及如何在命令行中使用它。

2 个答案:

答案 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的价值告诉你哪些失败。