Awk içinde bir dosya olup olmadığını nasıl kontrol edersiniz? [-d 'dosyaadı'] başarısız


10

Ben var olmayan bir ev dizini kümesi olan kullanıcıların bir listesini oluşturmaya çalışıyorum. Görünüşe göre bunu awk ile yapabilmeliyim, ancak sözdizimimde bir sorun var.

Bana "Geçersiz Sözdizimi" yazıyor. Neyi yanlış yapıyorum?

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

Muhtemelen kullanacağım son kod:

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

Benim de burada bir ilgili soru .

Yanıtlar:


14

Kullanabilirsin

system (komut) 
    İşletim sistemi komut komutunu yürütün ve ardından
    awk programına dön. İade komut ‘ın çıkış durumu. 

Örneğin:

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

Ben herhangi bir nedenle çalışmak için alamadım, bu yüzden bunu yapmak zorunda kaldı: awk '{if (sistem ("stat" $ 0 "> / dev / null 2> / dev / null") == 1) 0 $ yazdırın } '
Alexander Kjäll

1
@ AlexanderKjäll - bu bir dizinin varlığını test eder - normal bir dosyanın var olup olmadığını görmek için çalışıyorsanız , yukarıdaki kodun başarısız olacağı açıktır ...
don_crissti

3
Bu son derece tehlikelidir! Girişe bağlı olarak keyfi komutlar yürütülebilir . Örneğin, aşağıdaki yürütür /bin/ls: echo ';ls;' | awk '{ print system("[ ! -d " $1 " ]") }'
Tino

6

Bunun [ -d ]bir awkşey olduğunu düşünmüyorum , bu kabuk bir şey. Bunun yerine sadece bu şekilde yapardım:

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

Tabii ki, @Janis tarafından çok doğru bir şekilde işaret edildiği gibi, her şeyi kabukta yapabilirsiniz:

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

Tamam, sanırım dir gibi 6 $ geçmek ve -d $ dir yapmak istiyorum, ama bu çok yardımcı olur. Şimdi sadece "sonuç" yankı bulmak için nasıl bulmak için bulmak gerekir. Teşekkür ederim!
Doug

2
awkAslında gerekli olmadığını unutmayın ; zaten kabuk içinde döngü çünkü sadece yapabilirsiniz while IFS=: read -r name x uid x x x shell rest ; do ... ; done </etc/passwd.
Janis

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

@Janis gerçekten, iyi bir nokta, cevap düzenlendi.
terdon

@Terdon'un belirttiği gibi [ -d "$6"], aslında bash sözdizimi, awk değil. [Normal sözdizimi gibi görünüyor, ama (bash / Linux'un daha iyi weirdnesses birinde) bu aslında eşanlamlı olduğunu testçalıştırılabilir program (ya da belki yerleşik bunun sürümü bir bash ve sadece daha tuhaf olduğu, bash eşleşen bir gerektiriyor ]doesn o farkında olduğum hiçbir şeyi, her şeyin bir program değil, gerçekten sözdizimi olduğunu düşünmek için sizi yanıltmaktan başka bir şey yapmayın). Her durumda, awk'ın bildiği bir şey değil. Bu nedenle system(), anlaşıldığı yerde bash bağlamında başvurarak bu işleve erişmek için işleve ihtiyacınız vardır
Joe

6

Getline'ı kullanabilirsiniz :

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

1
Bu güvenlidir awk, ancak ne yazık ki dizinler için çalışmaz.
Tino

5

Gerçekten kullanıyorsanız gawk( kullanıyor olabilirsiniz nawkveya mawkbu durumda bu geçerli olmaz), v4.0'dan beri mevcut yüklenebilir uzantılardan birini kullanarak bunu yerel olarak yapabilirsiniz . Ben kullanıyorum (v4.0 yükleme uzantıları için sözdizimi üzerine bir çeşitleme vardı).gawk-4.1.x

Yükleme filefuncsuzantısı bir (diğerleri arasında) ekler stat()fonksiyonu:

@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)Bu uzantıyla ilgili ayrıntılar için kılavuz sayfasına bakın .

Gibi bir şey ile çalıştırın:

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

gawkİkili dosyanızın aşağıdakileri içeren uzantıları desteklediğini onaylayabilirsiniz :

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

Bir yana: dosyayı gawkokumak için küçük bir kütüphane işlevi ile birlikte gelir passwd, şöyle çağırabilirsiniz:

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

getentLinux / glibc sistemlerinde nsswitch'i desteklediğinden kullanmayı tercih ederim .


1
İlginç. Ben gawkv4 bu teklifleri farkında değildi . Teşekkürler!
Tino

3

Neredeyse garip ...

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

0

İşte bir çözüm

  • kullanır gawkve/bin/sh
  • dizin harici bir komutla kontrol ediyor mu
  • ama güvenli ve emniyetli bir yol yapar

Kod:

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

açıkladı:

  • IFS="" read -r dir; [ -d "$dir" ]; echo $?stdin'den bir yol okuyan ve bir dizinse çıktı veren bir kabuk kodudur 0, aksi takdirde1
  • print $6 |& cmddosya adını komuta yönlendirir. |&bir GNU-awk uzantısıdır.
  • cmd |& getline x komutun çıktısını GNU-awk'ye okur
  • close(cmd) komutu bitirir, böylece sonraki satır bir tane daha yürütebilir
  • if (x)yürütür printyalnızca xdeğil 0(dizin yapar böylece mevcut değil)

Yine de bu şekilde yapılmasını önermiyorum, çünkü çok yavaş ve beceriksiz. Ancak bu tarif güvenlidir , böylece düzensiz girişler zarar veremez. ( /etc/passwdZararlı veriler içerme olasılığı düşüktür , ancak belki de birileri güvenilir olmayan bir kaynaktan gelen verilerle kullanmak ister).

Eğer kullanamazsan gawk, bu talihsiz. Bu durumda sahip değilsiniz |&. Normal awkyalnızca aşağıdaki üç işlemden birini yapabilir:

  • print "data" | cmd; close(cmd): Verileri bir komuta aktarın
  • getline data < cmd; close(cmd): Bir komuttan veri okuma
  • ret = system(cmd): Komutun dönüş kodunu alın

"Normal" awksadece bir komut dosyasına veri aktaramaz ve aynı anda tekrar bir şey elde edemez (en azından bunun için bir yol bulamadım), bu yüzden daha sakar olan bir ara dosyaya (tempfile) ihtiyacınız var.

İlginç gözlem, böyle kolay bir görevin sadece kabukla da yapılabileceğidir:

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

Gerek yok bash, her normal Bourne kabuğu yukarıdaki kodu çalıştırabilir.

Yukarıdaki kabuk kodunun gerçekten gerekenden biraz daha ayrıntılı olduğunu unutmayın, ancak bu gerçekten onunla nasıl çalışılacağına dair bir işaretçi olacaktır (kabuğun nasıl çalıştığı konusunda tam olarak deneyimli olmayan kişiler için).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.