【问题标题】:How to skip a directory in awk?如何跳过awk中的目录?
【发布时间】:2016-03-05 05:42:09
【问题描述】:

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

$ 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

一切都很好,但* 也扩展到目录dirawk 尝试处理它。

所以我想知道: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"

但我不知道如何调用它,也不知道如何从命令行使用它。

【问题讨论】:

    标签: awk gawk dir


    【解决方案1】:

    我只是避免将目录传递给 awk,因为即使 POSIX 也说所有文件名参数都必须是文本文件。

    您可以使用find 来遍历目录:

    find PATH -type f -exec awk 'program' {} +
    

    【讨论】:

    • 是的!我认为这是最干净的方法。不过,我想知道awk 是否可以以任何方式做到这一点。我编辑了我的问题,因为我错误地使用了system(),所以现在它可以这样工作,但我仍然不喜欢为此调用外部命令的事实。
    • @fedorqui 我也用@load readdir 玩了一下(很高兴知道,谢谢).. 我得到了同样的结果,这意味着使用system() 来检查文件名是否是一个目录。我没有看到不同的方式。
    • 再次感谢赫克!我终于接受了 Ed Morton 的回答,因为它以一种 awk 的方式做到了。尽管建议一般不要这样做。
    • @fedorqui 好决定!他的回答很好!
    【解决方案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
    

    Per POSIX getline 返回 -1 如果/当它试图从文件中检索记录失败(例如,不可读的文件或文件不存在或文件是一个目录),你只需要 GNU awk 告诉你哪个在这些失败中,如果你关心的话,它是 ERRNO 的值。

    【讨论】:

    • 很好!所以目录上的getline不会直接失败但可以处理。
    • 对。当我第一次阅读您的问题时,我以为您正在尝试使用 awk 来搜索文件/目录(抱歉-注意力短暂!)但是在重新阅读时,您似乎只是想防止有人使用非文件调用脚本args - 这样做并没有错,以上就是你的做法。我已经更新了我的答案以更加支持这一点!
    • 是的,完全正确。这只是为了防止警告,甚至是退出代码,因为一个目录在一个所谓的文件列表中扩展。非常有趣的答案,我从中学到了很多,谢谢:)
    猜你喜欢
    • 2023-03-06
    • 2018-03-31
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 2016-04-11
    • 2012-07-26
    • 1970-01-01
    • 2012-06-22
    相关资源
    最近更新 更多