คุณจะตรวจสอบว่าไฟล์มีอยู่ใน awk ได้อย่างไร? [-d 'filename'] ล้มเหลว


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

และฉันมีคำถามที่เกี่ยวข้องที่นี่

คำตอบ:


14

คุณสามารถใช้

ระบบ (คำสั่ง) 
    ดำเนินการคำสั่งระบบปฏิบัติการคำสั่งแล้ว
    กลับไปที่โปรแกรม awk สถานะการออกของ 
 คำสั่ง Return

เช่น:

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

ฉันไม่สามารถทำงานนี้ด้วยเหตุผลบางอย่างดังนั้นฉันต้องทำเช่นนี้: awk '{if (ระบบ ("สถิติ" $ 0 "> / dev / null 2> / dev / null") == 1) พิมพ์ $ 0 } '
Alexander Kjäll

1
@ AlexanderKjäll - การทดสอบนี้สำหรับการมีอยู่ของไดเรกทอรี - หากคุณพยายามที่จะดูว่ามีไฟล์ปกติอยู่หรือไม่ก็เป็นที่ชัดเจนว่ารหัสข้างต้นจะล้มเหลว ...
don_crissti

3
มันอันตรายมาก! ขึ้นอยู่กับคำสั่งโดยพลการอินพุตอาจถูกเรียกใช้งาน ตัวอย่างเช่นการดำเนินการดังต่อไปนี้/bin/ls: echo ';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 ชี้ให้เห็นอย่างถูกต้องคุณสามารถทำทุกอย่างในเปลือกได้:

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ไม่จำเป็นจริง ๆ 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"]เป็นจริงทุบตีไวยากรณ์ไม่ใช่ awk [ดูเหมือนไวยากรณ์ปกติ แต่ (ในหนึ่งในทุบตี / ลินุกซ์ weirdnesses ดีกว่า) ก็จริงไวพจน์สำหรับtestโปรแกรมปฏิบัติการ (หรืออาจจะทุบตีในตัวรุ่นของมันและเพียงเพื่อจะประหลาด, ทุบตีต้องมีการจับคู่]ที่ doesn ฉันไม่ได้ทำอะไรเลยนอกจากจะทำให้คุณเข้าใจผิดคิดว่าเรื่องทั้งหมดเป็นเรื่องของไวยากรณ์และไม่ใช่โปรแกรม) ไม่ว่าในกรณีใดมันไม่ใช่สิ่งที่ awk รู้เกี่ยวกับ นั่นเป็นเหตุผลที่คุณต้องการsystem()ฟังก์ชั่นในการเข้าถึงโดยการอ้างอิงในบริบทของการทุบตีซึ่งเป็นที่เข้าใจ
Joe

6

คุณสามารถใช้getline :

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

1
สิ่งนี้ปลอดภัยawkแต่น่าเสียดายที่ไม่สามารถใช้ได้กับไดเรกทอรี
Tino

5

หากคุณกำลังใช้งานจริง ๆgawk(แม้ว่าคุณอาจกำลังใช้nawkงานอยู่หรือmawkในกรณีนี้จะไม่มีผลใช้งาน) คุณสามารถทำได้โดยใช้ส่วนขยายที่สามารถโหลดได้ซึ่งมีให้ตั้งแต่ v4.0 ฉันกำลังใช้gawk-4.1.x(v4.0 มีรูปแบบเกี่ยวกับไวยากรณ์สำหรับการโหลดส่วนขยาย)

การโหลด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)man page สำหรับรายละเอียดเกี่ยวกับส่วนขยายนี้

ทำงานด้วยสิ่งที่ชอบ:

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
น่าสนใจ ฉันไม่ได้ตระหนักถึงgawkข้อเสนอ v4 นี้ ขอบคุณ!
Tino


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 $?เป็นรหัสเปลือกซึ่งอ่านเส้นทางจาก 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ไม่สามารถไพพ์ข้อมูลลงในสคริปต์และนำบางสิ่งออกไปอีกครั้งในเวลาเดียวกัน (อย่างน้อยฉันก็ไม่ได้หาวิธีแก้ไข) ดังนั้นคุณต้องใช้ไฟล์ระดับกลาง (tempfile) ซึ่งค่อนข้างงุ่มง่ามกว่า

การสังเกตที่น่าสนใจคือการทำเปลือกหอยอย่างง่ายสามารถทำได้ด้วยตัวเอง:

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

คุณไม่ต้องการbashBourne-shell ธรรมดาแต่ละตัวสามารถเรียกใช้งานโค้ดด้านบน

โปรดทราบว่ารหัสเชลล์ด้านบนนั้นซับซ้อนกว่าที่จำเป็นจริงๆเล็กน้อย แต่นี่จะเป็นตัวชี้ว่าจะทำงานกับมันอย่างไร (สำหรับคนที่ไม่เคยมีประสบการณ์ในการทำงานของเชลล์มาก่อน)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.