Come si controlla se esiste un file in awk? [-d 'nomefile'] non riuscito


10

Sto cercando di generare un elenco di utenti con un set di directory home che non esiste. Sembra che dovrei essere in grado di farlo con awk, ma qualcosa non va nella mia sintassi.

Continua a dirmi "Sintassi non valida" in]. Che cosa sto facendo di sbagliato?

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

Il codice finale che probabilmente finirò per usare è:

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

E ho una domanda correlata qui .

Risposte:


14

Puoi usare

system (comando) 
    Eseguire il comando di comando del sistema operativo e quindi
    tornare al programma awk. Stato di uscita del comando di ritorno . 

per esempio:

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

Non sono riuscito a farlo funzionare per qualche motivo, quindi ho dovuto farlo: awk '{if (system ("stat" $ 0 "> / dev / null 2> / dev / null") == 1) print $ 0 } "
Alexander Kjäll il

1
@ AlexanderKjäll - questo test per l'esistenza di una directory - se stai cercando di vedere se esiste un file normale , allora è ovvio che il codice sopra fallirà ...
don_crissti

3
Questo è estremamente pericoloso! A seconda dell'ingresso possono essere eseguiti comandi arbitrari . Ad esempio, segue /bin/ls: echo ';ls;' | awk '{ print system("[ ! -d " $1 " ]") }'
Tino

6

Non penso che [ -d ]sia una awkcosa, è una cosa da guscio. Vorrei solo farlo in questo modo invece:

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

Certo, come sottolineato correttamente da @Janis, puoi fare tutto nella shell:

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

Ok, penso di voler passare $ 6 come dir, e fare -d $ dir, ma questo aiuta molto. Ora devo solo capire come fare eco "nessun risultato" se non ne viene trovato nessuno. Grazie!
Doug,

2
Si noti che awknon è effettivamente necessario; dato che comunque esegui il loop in shell, puoi anche semplicemente farlo while IFS=: read -r name x uid x x x shell rest ; do ... ; done </etc/passwd.
Janis,

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

@Janis davvero, buon punto, risposta modificata.
terdon

Come osserva @terdon, in [ -d "$6"]realtà è sintassi bash, non awk. Le [somiglia alla sintassi normale, ma (in una delle bash / weirdnesses migliori di Linux) è in realtà un sinonimo per il testprogramma eseguibile (o forse un bash built-in versione di esso, e, tanto per essere più strano, bash richiede un abbinamento ]che doesn non fare nulla di cui sono a conoscenza se non quello di indurti a pensare che l'intera cosa sia davvero sintassi e non un programma). In ogni caso, non è qualcosa che Awk conosce. Ecco perché è necessaria la system()funzione per accedervi facendo riferimento a esso nel contesto di bash dove è compreso
Joe

6

Puoi usare getline :

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

1
Questo è sicuro awk, ma purtroppo non funziona per le directory.
Tino

5

Se stai davvero utilizzando gawk(anche se potresti utilizzarlo nawko mawk, nel qual caso ciò non si applica), puoi farlo nativamente usando una delle estensioni caricabili disponibili dalla v4.0. Sto usando gawk-4.1.x(la versione 4.0 aveva una variazione sulla sintassi per il caricamento delle estensioni).

Caricando l' filefuncsestensione si aggiunge (tra gli altri) una stat()funzione:

@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)
   }
}

Vedi la filefuncs(3am)pagina man per i dettagli su questa estensione.

Corri con qualcosa del tipo:

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

Puoi confermare che il tuo gawkbinario supporta le estensioni con:

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

A parte: include gawkanche una piccola funzione di libreria per leggere il passwdfile, puoi invocarlo come:

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

Preferisco usare getentsu sistemi Linux / glibc in quanto supporta nsswitch.


1
Interessante. Non ero a conoscenza delle gawkofferte di v4 questo. Grazie!
Tino,

3

È quasi imbarazzante ...

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

0

Ecco una soluzione che

  • usa gawke/bin/sh
  • la directory controlla con qualche comando esterno
  • ma lo fa in modo sicuro

Codice:

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

ha spiegato:

  • IFS="" read -r dir; [ -d "$dir" ]; echo $?è un codice shell che legge un percorso da stdin e genera 0se è una directory, altrimenti1
  • print $6 |& cmdreindirizza il nome file al comando. |&è un'estensione GNU-awk.
  • cmd |& getline x legge l'output del comando in GNU-awk
  • close(cmd) termina il comando, quindi la riga successiva può eseguirne di nuovo uno
  • if (x)esegue il printsolo se xnon lo è 0(quindi la directory non esiste)

Non consiglio di farlo in questo modo, però, perché è molto lento e goffo. Ma questa ricetta è sicura , in modo che input irregolari non possano fare del male. (È improbabile che /etc/passwdcontenga dati dannosi, ma forse qualcuno vuole usarli con dati provenienti da una fonte non attendibile).

Se non puoi usarlo gawk, questo è un peccato. In tal caso non hai |&. Normale awkpuò eseguire solo una delle seguenti tre operazioni:

  • print "data" | cmd; close(cmd): Convoglia i dati in un comando
  • getline data < cmd; close(cmd): Legge i dati da un comando
  • ret = system(cmd): Ottiene il codice di ritorno del comando

"Normale" awksemplicemente non può reindirizzare i dati in uno script e ricavarne qualcosa di nuovo allo stesso tempo (almeno non ho trovato un modo per farlo), quindi hai bisogno di un file intermedio (tempfile) che è ancora più goffo.

L'osservazione interessante è che un compito così semplice può essere svolto anche con la sola 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

Non è necessario bash, ogni normale Bourne-shell può eseguire il codice sopra.

Si noti che sopra il codice della shell è un po 'più elaborato di quanto realmente necessario, ma questo dovrebbe essere un puntatore su come lavorare davvero con esso (per le persone che non hanno esperienza completa sul funzionamento della shell).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.