Como você verifica se um arquivo existe no awk? [-d 'filename'] falhando

10

Estou tentando gerar uma lista de usuários que possuem um conjunto de diretórios base que não existe. Parece que eu deveria ser capaz de fazer isso com o awk, mas algo está errado com a minha sintaxe.

Ele continua me dizendo "Sintaxe Inválida" no]. O que estou fazendo errado?

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

O código final que provavelmente vou usar é:

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

E eu tenho uma questão relacionada aqui .

    
por Doug 08.04.2015 / 23:27

6 respostas

12

Você poderia usar

system(command)
    Execute the operating-system command command and then
    return to the awk program. Return command’s exit status. 

por exemplo:

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

Eu não acho que [ -d ] seja uma coisa awk , isso é uma coisa de shell. Eu apenas faria assim:

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

Claro, como muito corretamente apontado por @Janis, você pode fazer tudo no shell:

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

Você pode usar getline :

awk 'BEGIN {print getline < "file" < 0 ? "not exists" : "exists"}'
    
por 17.02.2016 / 02:54
3

É quase awk ...

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

Se você estiver realmente usando gawk (embora você possa estar usando nawk , ou mawk , caso em que isso não se aplica), você pode fazer isso nativamente usando um das extensões carregáveis disponíveis desde a v4.0 . Estou usando gawk-4.1.x (v4.0 tinha uma variação na sintaxe para carregar extensões).

O carregamento da extensão filefuncs adiciona (entre outros) uma função 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)
      }
}

Veja a página filefuncs(3am) man para detalhes sobre esta extensão.

Corra com algo como:

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

Você pode confirmar que o seu binário gawk suporta extensões com:

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"])
}

Além de: gawk também vem com uma pequena função de biblioteca para ler o arquivo passwd , você pode invocá-lo como:

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

Eu prefiro usar getent em sistemas Linux, pois ele suporta nsswitch.

    
por 16.04.2015 / 14:35
0

Aqui está uma solução que

  • usa gawk e /bin/sh
  • o diretório verifica com algum comando externo
  • mas é uma maneira segura e protegida

Código:

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

explicado:

  • IFS="" read -r dir; [ -d "$dir" ]; echo $? é um código de shell que lê um caminho de stdin e gera 0 se for um diretório, senão 1
  • print $6 |& cmd canaliza o nome do arquivo para o comando. |& é uma extensão do GNU-awk.
  • cmd |& getline x lê a saída do comando no GNU-awk
  • close(cmd) termina o comando, então a próxima linha pode executar novamente
  • if (x) executa o print apenas se x não for 0 (portanto, o diretório não existe)

Eu não recomendo fazer assim, porque é muito lento e desajeitado. Mas esta receita é segura , tal que a entrada irregular não pode causar danos. (É improvável que /etc/passwd contenha dados prejudiciais, mas talvez alguém queira usá-lo com dados de uma fonte não confiável).

Se você não pode usar gawk , isso é lamentável. Nesse caso, você não tem |& . Somente awk normal pode fazer um dos três seguintes:

  • print "data" | cmd; close(cmd) : dados de tubulação em um comando
  • getline data < cmd; close(cmd) : ler dados de um comando
  • ret = system(cmd) : obtenha o código de retorno do comando

"Normal" awk simplesmente não pode enviar dados para um script e obter algo novamente ao mesmo tempo (pelo menos eu não encontrei um caminho para isso), então você precisa de algum arquivo intermediário (tempfile) que é ainda mais desajeitado.

A observação interessante é que uma tarefa tão fácil pode ser feita apenas com o 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

Você não precisa de bash , cada Bourne-shell normal pode executar o código acima.

Observe que o código shell acima é um pouco mais elaborado do que o realmente necessário, mas isso deve ser um indicador de como realmente trabalhar com ele (para pessoas que não têm experiência completa em como o shell funciona).

    
por 02.01.2018 / 00:07