awk检查文件是否存在。

6
printf "2015-03-02|/home/user/.ssh/config\n2015-03-02|/home/user/Desktop/temp328\n" | awk -F\| 'if ( -f $2 )  { print $2}'

或者
printf "2015-03-02|/home/user/.ssh/config\n2015-03-02|/home/user/Desktop/temp328\n" | awk -F\| '{if (system("test -f" $2)) print $2}'

/home/user/.ssh/config\n2015-03-02 - 存在

/home/user/Desktop/temp328 - 已删除

我想只打印存在的文件,但这些命令不起作用。


3
你非常需要使用 awk 来完成这件事吗? - Pankrates
2
a) 这不是 awk 的工作,而是 shell 的工作。 b) 你所写的内容都不是 awk 语法。请编辑你的问题解释为什么要使用 awk。 - Ed Morton
7个回答

6
第二次尝试相当接近了,你需要在 test -f 后面加一个空格。
base$ echo '2015|/etc/mtab
> 2015|/etc/ntab' | awk -F\| '{ if (system("test -f " $2)) print $2}'
/etc/ntab

您可能希望倒置使用 if (system(...)==0) 以获得您预期的语义。此外,更为优雅的是,Awk需要在大括号外面设置条件,因此您可以避免明确使用 if
awk -F\| 'system("test -f " $2)==0 { print $2 }'

我同意评论者的看法,使用Awk来处理这个问题有点不切实际。

如果像评论中提到的那样,您需要处理任意文件名,您可以添加代码来引用任何特殊的Shell字符:

awk -F\| 'system ("test -f " gensub(/[^\/A-Za-z0-9]/, "\\\\&", "g", $2))==0 {
   print $2 }'   # caveat: gensub() is gawk only

...但是您整体的解决方案不能处理包含换行符或管道符号的文件名(因为您将它们分别用作记录和字段分隔符),所以再次放弃Awk并采用不同的方法可能是明智的选择。

(替换中的字符类是不完整的;有各种标点符号等可以添加,我可能会错过一些重要的内容;但是快速检查表明,多余的反斜杠应该是无害的。如果您没有Gawk,请参见此处,或者再次考虑放弃这种方法。)

while IFS='|' read -r stuff filename; do
    test -f "$filename" && echo "$filename"
done <<':'
2015|/etc/mtab
2016|/etc/ntab
2017|/path/to/file with whitespace in name
2018|/path/to/file\with[funny"characters*in(file'name|even pipes, you see?
:

(仍然没有换行的方法,但其他方面都应该没问题。)

如果$2="file 2"或者$2="filel|-da",那么这个命令就很不错。但是如果是这样的话,shell会写入错误信息:/bin/sh: 1: l-da: not found。 - pepco2
如果你需要处理奇怪的文件名,你不能确定它们不包含单引号或换行符。虽然你可以调整代码以应对这些情况,但我认为你可能需要重新考虑你的方法。但对于即时问题,在system命令中添加单引号来包围文件名,或者在将其传递给system之前添加代码以反斜杠转义文件名中的所有shell特殊字符。 - tripleee

5

使用GNU awk,您可以使用包含在 filefuncs 扩展中的 stat()

$ ls -l 
-rw-r--r-- 1 james james 4 Oct  3 12:48 foo
-rw------- 1 root  root  0 Oct  3 12:48 bar

Awk:

$ awk -v file=foo '
@load "filefuncs"
BEGIN {
    ret=stat(file,fdata)
    printf "ret:  %d\nsize: %d\n",ret,fdata["size"]
}'
-v file= foo的输出结果为:
ret:  0
size: 4

对于bar

ret:  0
size: 0

对于不存在的baz

ret:  -1
size: 0

1

在awk中检查可读文件的存在非常容易,而不必采用system()生成某些内容。只需尝试从文件中读取即可。

从awk的手册页面(在我的系统上):

在所有情况下,getline返回1表示成功输入,0表示文件结尾,-1表示错误。

因此,以下是一些示例代码。

#!/usr/bin/awk -f

function file_exists(file) {
  n=(getline _ < file);
  if (n > 0) {
    print "Found: " file;
    return 1;
  } else if (n == 0) {
    print "Empty: " file;
    return 1;
  } else {
    print "Error: " file;
    return 0;
  }
}

BEGIN {

  file_exists(ARGV[1]);

}

给我这些结果:
$ touch /tmp/empty
$ touch /tmp/noperm ; chmod 000 /tmp/noperm
$ ./check.awk /etc/passwd
Found: /etc/passwd
$ ./check.awk /nonexistent
Error: /nonexistent
$ ./check.awk /tmp/empty
Empty: /tmp/empty
$ ./check.awk /tmp/noperm
Error: /tmp/noperm

使用您的示例数据:
$ fmt="2015-03-02|/home/user/.ssh/config\n2015-03-02|/home/user/Desktop/temp328\n"
$ printf "$fmt" | cut -d\| -f2 | xargs -n 1 ./check.awk
Error: /home/user/.ssh/config
Error: /home/user/Desktop/temp328

如果要用于更广泛的目的,您可以将此函数缩短为类似以下内容:

function file_exists(file) {
  if ((getline _ < file) >= 0) { return 1; }
}

它并不会告诉你文件是否存在,只是能够读取该文件中的一行。如果文件存在但是为空或无法读取,则会失败。 - Ed Morton
好的,谢谢。我提到了非零长度部分,但我已经更新了我的答案中的措辞,包括“可读”条件,并改进了awk函数以处理“空”情况。 - ghoti
1
如果getline成功,且文件是二进制的并且前N个字节没有换行符,它可以消耗任意大量的内存。当然,Perl的-e运算符使这变得微不足道,但OP询问了awk。 - Keith Thompson

0
在GNU AWK中,有一个可加载的C语言库“filefuncs”。它可以加载关于文件、目录、套接字等文件系统数据。我认为获取有关文件信息的快速方法不是使用外部调用,而是使用内部函数。
#!/usr/bin/gawk -f
@load "filefuncs"
 function exist(file){
  return stat(file, null)
 }
BEGIN{
 print exist("/etc/passwd")}

如果文件存在,则返回'0',否则返回:'-1'
'null' - 数组的任意自由名称(需要第二个参数!)
如果您不想使用任何函数,那么就这样:

#!/usr/bin/gawk -f
@load "filefuncs"
BEGIN{print stat("/etc/passwd", null)}

0

我从另一个帖子中重新粘贴我的答案,因为它在检查文件方面似乎是相关的。我主要添加了关于如何利用system()执行奇怪操作的通用情况。

实际上,在某些情况下,您确实可以利用system()直接获得所需的输出,而无需处理格式化命令,通过getline运行它,暂时存储它,重置RS(如果您之前将其设置为“^ $”),并在返回输出之前关闭该命令,如下:

-rw-r--r--  1 501  20  77079 Jul 26 13:07 ./selectWoo.full.min.js.txt

valid file :: exist_and_non_empty

non-existent file :: cannot locate

32297  gprintf '\033c\033[3J'; echo; ls -lFGnd "./selectWoo.full.min.js"*; 
       mawk2 'function filetest(fn) { 
          gsub(/\047/,"&\134\047&",fn); # in case single-qt in filename
          return 
              system(" exit \140 [ -r \047"(fn)"\047 ] \140 ") 
              ? "cannot locate" 
              : "exist_and_non_empty" 
       } BEGIN { 
           ORS = "\n\n"; 
           fn_pfx="./selectWoo.full.min.js";
           print "\nvalid file :: "      filetest(fn_pfx ".txt"); 
           print "non-existent file :: " filetest(fn_pfx ".txt_fake") 
      }' ; 
      history 1 ; echo

这里我只是为了说明而更加详细地阐述。我们直接将退出码设置为文件测试的退出码,而不是返回system()调用是否成功。

如果你想简化返回值为布尔型,那么可以这样写:

return ! system(…)

  • 我没有测试过所有的 POSIX 文件/目录信息检查标志,但我想象中可能会失败的标志不会超过几个。

你也可以执行其他任务,只要输出是非负整数(假设它们在返回之前执行exit_code % 256,只要你能够理解该输出即可。快速示例(\047是单引号'\045是百分号%,140是重音符[`]

mawk2 'BEGIN { a = "0123456789ABCDEF"; print 
    system(" exit \140 printf \047\045s\047 \047"(a)"\047
             | wc -c \140 "); }'

能够正确打印出字符串长度为"16"。

我非常清楚这是一种糟糕的使用system()和POSIX退出码的方式。


0

你可以轻松地使用BASH,并将结果馈送/管道传输到AWK。

% ls
file_list file1 file3
% cat file_list
file1
file2
file3
file4
% cat file_list | bash -c 'while read file ; do [ -f "$file" ] || echo "No file: $file"; done'
No file: file2
No file: file4

0

这并不是我的答案,但这里还没有记录。来自“The GNU Awk User's Guide”:

给出了以下方法:

  # readable.awk --- library file to skip over unreadable files

  BEGIN {
      for (i = 1; i < ARGC; i++) {
          if (ARGV[i] ~ /^[[:alpha:]_][[:alnum:]_]*=.*/ \
              || ARGV[i] == "-" || ARGV[i] == "/dev/stdin")
              continue    # assignment or standard input
          else if ((getline junk < ARGV[i]) < 0) # unreadable
              delete ARGV[i]
          else
              close(ARGV[i])
      }
  }

这段代码实际上是处理命令行的。对于本问题有用的是else if ...部分。
   else if ((getline junk < ARGV[i]) < 0) # unreadable
        delete ARGV[i]
      :

这基本上是在名为ARGV[i]的文件上执行readline,当它失败时,它们会删除数组元素。文件不存在或不可读。

无论哪种方式,您都不能使用它。所有操作都在同一个aWk进程中完成,没有对shell的执行等。

我今天需要这个功能,所以我写了下面这个小函数:

  ##  file_exist
  #     * ref: [12.3.3 Checking for Readable Data Files](http://langevin.univ-tln.fr/cours/COMPIL/tps/awk.html#File-Checking)
  #         o [The GNU Awk User's Guide](http://langevin.univ-tln.fr/cours/COMPIL/tps/awk.html)
  #

  function file_exist(  file_path, _rslt, _junk  )
  {
      _rslt = (0==1);     #   false

      if( (getline _junk < file_path) > 0)  )    ## readable 
      {
          _rslt = (1==1);
          close( file_path );
      }
      return _rslt;
  }

注意:
  • 当文件为空时,函数返回TRUE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接