如何检查awk中是否存在文件?[-d'文件名']失败


10

我正在尝试生成具有不存在的主目录集的用户列表。看来我应该可以用awk做到这一点,但是我的语法有问题。

它总是在[]处告诉我“无效语法”。我究竟做错了什么?

awk -F: '{ if(![ -d "$6"]){ print $1 " " $3 " " $7}}' /etc/passwd

我可能最终要使用的最终代码是:

awk -F: '{if(system( "[ -d " $6 " ]") == 1 && $7 != "/sbin/nologin" ) {print "The directory " $6 " does not exist for user " $1 }}' /etc/passwd

在这里一个相关的问题

Answers:


14

你可以用

系统(命令) 
    执行操作系统命令命令,然后
    返回awk程序。返回命令的退出状态。 

例如:

awk -F: '{if(system("[ ! -d " $6 " ]") == 0) {print $1 " " $3 " " $7}}' /etc/passwd

由于某种原因,我无法使它正常工作,所以我不得不这样做:awk'{if(system(“ stat” $ 0“> / dev / null 2> / dev / null”)== 1)打印$ 0 }'
AlexanderKjäll17年

1
@AlexanderKjäll-测试目录是否存在-如果您尝试查看常规文件是否存在,则上述代码显然会失败...
don_crissti

3
这是非常危险的! 根据输入,可以执行任意命令。例如,执行以下命令/bin/lsecho ';ls;' | awk '{ print system("[ ! -d " $1 " ]") }'
Tino

6

我不认为这[ -d ]是一件awk事情,而是一件空壳的事情。我会用这种方式代替:

awk -F: '{ print $1,$3,$7}' /etc/passwd | 
    while read name uid shell; do 
        [ -d "/home/$name" ] || echo "$name $uid $shell"; 
    done

当然,正如@Janis正确指出的那样,您可以在shell中完成所有操作:

while IFS=: read  name x uid x x x shell rest; do
     [ -d "/home/$name" ] || echo "$name $uid $shell" 
done < /etc/passwd

好的,我想我实际上想将$ 6作为dir传递,并执行-d $ dir,但这很有帮助。现在,我只需要弄清楚如果找不到任何结果,该如何回显“无结果”。谢谢!
Doug

2
请注意,awk实际上并不需要。因为无论如何你在shell中循环,你也可以做while IFS=: read -r name x uid x x x shell rest ; do ... ; done </etc/passwd
Janis

@Doug就做awk -F: '{print $6}' /etc/passwd | while read dir; do [ -d "$dir" ] || echo "no results for $dir"; done
terdon

的确,@ Janis很好,答案已编辑。
terdon

如@terdon所述,[ -d "$6"]实际上是bash语法,而不是awk。在[貌似正常的语法,但(在bash / linux的更好weirdnesses之一),它实际上是对一个同义词test可执行程序(或者内置的版本,它是bash,并且,仅仅是怪异,庆典需要一个匹配]的是没有按除了误导您以为整个事情实际上是语法而不是程序之外,我什么也不会做。无论如何,awk都不了解。这就是为什么你需要的system()功能来访问它通过在bash的情况下引用它其中可以理解

6

您可以使用getline

awk 'BEGIN {print getline < "file" < 0 ? "not exists" : "exists"}'

1
这在中是安全的awk,但不幸的是不适用于目录。
蒂诺

5

如果您确实在使用gawk(尽管可能使用nawkmawk,在这种情况下将不适用),则可以使用v4.0以后可用的可加载扩展之一来原生地执行此操作。我正在使用(v4.0在加载扩展的语法上有所变化)。gawk-4.1.x

加载filefuncs扩展会添加(以及其他)stat()功能:

@load "filefuncs"
BEGIN {FS=":"}
(NF==7) {
   printf("user: %s %i %i\n",$1,$3,$4)
   rc=stat($6,fstat)
   err=ERRNO  # ERRNO is a string, not an int!
   if (rc<0) { 
       printf(" error: %s rc=%i %s\n",$6,rc,err)
   } else {
      if (fstat["type"]!="directory") 
        printf("  ENOTDIR: %s %s\n",$6,fstat["type"])
      if (fstat["uid"]!=$3) 
        printf("  uid mismatch: %s %i!=%i\n",$6,fstat["uid"],$3)
      if (fstat["gid"]!=$4) 
        printf("  gid mismatch: %s %i!=%i\n",$6,fstat["gid"],$4)
   }
}

有关filefuncs(3am)此扩展的详细信息,请参见手册页。

运行类似:

gawk -f testhome.awk <(getent passwd)    # bash/zsh and glibc
gawk -f testhome.awk /etc/passwd

您可以通过以下方式确认您的gawk二进制文件支持扩展:

BEGIN { 
  if (!("api_major" in PROCINFO)) 
    printf("No extension API.\n")
  else
    printf("Extension API v%s.%s.\n",PROCINFO["api_major"],PROCINFO["api_minor"])
}

另外:gawk还带有一个小的库函数来读取passwd文件,您可以像这样调用它:

gawk -i passwd.awk -- 'BEGIN { while(uu=getpwent()) {print uu;} endpwent(); }'

我更喜欢getent在Linux / glibc系统上使用,因为它支持nsswitch。


1
有趣。我不知道gawkv4提供了此功能。谢谢!
蒂诺

3

快到了...

perl -F: -ane 'if(!-d $F[5]){ print "$F[0] $F[2] $F[6]" }' /etc/passwd

0

这是一个解决方案

  • 使用gawk/bin/sh
  • 目录是否使用某些外部命令进行检查
  • 但这是一种安全可靠的方法吗

码:

gawk -F: '{
    cmd="IFS=\"\" read -r dir; [ -d \"$dir\" ]; echo $?";
    print $6 |& cmd;
    cmd |& getline x;
    close(cmd);
    if (x) print x " " $1 " " $3 " " $7
}' /etc/passwd

解释:

  • IFS="" read -r dir; [ -d "$dir" ]; echo $?是一个Shell代码,它从stdin读取路径并输出(0如果它是目录),否则1
  • print $6 |& cmd将文件名传递给命令。|&是GNU-awk扩展。
  • cmd |& getline x 将命令的输出读取到GNU-awk
  • close(cmd) 完成命令,因此下一行可以再次执行
  • if (x)执行print仅当x没有0(这样的目录不存在)

但是,我不建议这样做,因为它非常缓慢且笨拙。但是此配方是安全的,这样不规则的输入不会造成伤害。(不太可能/etc/passwd包含有害数据,但也许有人想将其与来自非受信任来源的数据一起使用)。

如果您不能使用gawk,这是不幸的。那样的话你就没有了|&。普通awk只能执行以下三个操作之一:

  • print "data" | cmd; close(cmd):将数据传送到命令中
  • getline data < cmd; close(cmd):从命令读取数据
  • ret = system(cmd):获取命令的返回码

“普通” awk根本无法将数据通过管道传输到脚本中并同时从中获取某些信息(至少我没有找到解决方法),因此您需要一些更加笨拙的中间文件(临时文件)。

有趣的发现是,仅使用shell也可以完成如此​​简单的任务:

dump_problems()
{
have=0;
while IFS=: read -r user pw id grp gcos dir shl;
do
  [ -d "$dir" ] && continue;
  echo "$user $id $shl";
  have=1;
done </etc/passwd;
return $have;
}

dump_problems && echo ALL OK >&2 || echo "Problems were found" >&2

您不需要bash,每个普通的Bourne-shell都可以执行上述代码。

请注意,上面的shell代码比实际需要的要复杂得多,但这将是如何真正使用它的指针(对于那些不完全了解shell如何工作的人)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.