Làm thế nào để bạn kiểm tra nếu một tập tin tồn tại trong awk? [-d 'tên tệp'] không thành công


10

Tôi đang cố gắng tạo danh sách người dùng có bộ thư mục chính không tồn tại. Có vẻ như tôi có thể làm điều này với awk, nhưng có gì đó không đúng với cú pháp của tôi.

Nó liên tục nói với tôi "Cú pháp không hợp lệ" tại]. Tôi đang làm gì sai?

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

Mã cuối cùng tôi có thể sẽ sử dụng là:

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

Và tôi có một câu hỏi liên quan ở đây .

Câu trả lời:


14

Bạn đã có thể sử dụng

hệ thống (lệnh) 
    Thực hiện lệnh điều hành hệ thống chỉ huy và sau đó
    trở lại chương trình awk. Trả về lệnh Thoát trạng thái. 

ví dụ:

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

Tôi không thể làm việc này vì một số lý do, vì vậy tôi phải làm điều này: awk '{if (system ("stat" $ 0 "> / dev / null 2> / dev / null") == 1) in $ 0 } '
Alexander Kjäll

1
@ AlexanderKjäll - kiểm tra sự tồn tại của một thư mục - nếu bạn đang cố gắng xem liệu một tệp thông thường có tồn tại hay không thì rõ ràng mã trên sẽ thất bại ...
don_crissti

2
Điều này cực kỳ nguy hiểm! Tùy thuộc vào các lệnh tùy ý đầu vào có thể được thực thi . Ví dụ: các lần thực hiện sau /bin/ls: echo ';ls;' | awk '{ print system("[ ! -d " $1 " ]") }'
Tino

6

Tôi không nghĩ đó [ -d ]là một awkthứ, đó là một thứ vỏ. Tôi sẽ chỉ làm theo cách này thay vào đó:

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

Tất nhiên, như được chỉ ra rất chính xác bởi @Janis, bạn có thể thực hiện toàn bộ nội dung trong trình bao:

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

Ok, tôi nghĩ rằng tôi thực sự muốn vượt qua $ 6 dưới dạng dir và làm -d $ dir, nhưng điều này giúp ích rất nhiều. Bây giờ tôi chỉ cần tìm ra cách lặp lại "không có kết quả" nếu không tìm thấy. Cảm ơn bạn!
Doug

2
Lưu ý rằng awkkhông thực sự cần thiết; vì bạn lặp trong shell dù sao bạn cũng có thể làm while IFS=: read -r name x uid x x x shell rest ; do ... ; done </etc/passwd.
Janis

@Doug Chỉ cần làm awk -F: '{print $6}' /etc/passwd | while read dir; do [ -d "$dir" ] || echo "no results for $dir"; done.
terdon

@Janis thực sự, điểm tốt, câu trả lời chỉnh sửa.
terdon

Như @terdon lưu ý, [ -d "$6"]thực sự là cú pháp bash, không phải awk. Các [trông giống như cú pháp bình thường, nhưng (theo một trong bash / Linux weirdnesses tốt hơn) nó thực sự là một từ đồng nghĩa với các testchương trình thực thi (hoặc có thể là một bash tích hợp trong phiên bản của nó, và chỉ để được kì quặc, bash đòi hỏi một kết hợp ]mà doesn Tôi không làm bất cứ điều gì tôi biết ngoài việc đánh lừa bạn để nghĩ rằng toàn bộ điều này thực sự là cú pháp chứ không phải là một chương trình). Trong mọi trường hợp, đó không phải là điều mà awk biết. Đó là lý do tại sao bạn cần system()chức năng để truy cập nó bằng cách tham chiếu nó trong bối cảnh bash nơi nó được hiểu
Joe

6

Bạn có thể sử dụng getline :

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

1
Điều này là an toàn awk, nhưng tiếc là không hoạt động cho các thư mục.
Tino

5

Nếu bạn đang thực sự sử dụng gawk(mặc dù bạn có thể đang sử dụng nawkhoặc mawktrong trường hợp này sẽ không áp dụng), bạn có thể thực hiện việc này một cách tự nhiên bằng cách sử dụng một trong các tiện ích mở rộng có thể tải được kể từ phiên bản 4.0. Tôi đang sử dụng gawk-4.1.x(v4.0 có một biến thể về cú pháp để tải tiện ích mở rộng).

Tải filefuncstiện ích mở rộng thêm (trong số những người khác) một stat()chức năng:

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

Xem filefuncs(3am)trang người đàn ông để biết chi tiết về phần mở rộng này.

Chạy với một cái gì đó như:

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

Bạn có thể xác nhận rằng gawknhị phân của bạn hỗ trợ các tiện ích mở rộng với:

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

Ngoài ra: gawkcũng đi kèm với một chức năng thư viện nhỏ để đọc passwdtệp, bạn có thể gọi nó như sau:

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

Tôi thích sử dụng getenttrên các hệ thống Linux / glibc vì nó hỗ trợ nsswitch.


1
Hấp dẫn. Tôi đã không nhận thức được gawkcung cấp v4 này. Cảm ơn!
Tino

3

Nó gần như là ...

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

0

Đây là một giải pháp

  • sử dụng gawk/bin/sh
  • thư mục kiểm tra với một số lệnh bên ngoài
  • nhưng nó là một cách an toàn và an toàn

Mã số:

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

giải thích:

  • IFS="" read -r dir; [ -d "$dir" ]; echo $?là một mã shell đọc một đường dẫn từ stdin và xuất ra 0nếu nó là một thư mục, khác1
  • print $6 |& cmdđặt tên tệp cho lệnh. |&là một phần mở rộng GNU-awk.
  • cmd |& getline x đọc đầu ra của lệnh vào GNU-awk
  • close(cmd) kết thúc lệnh, vì vậy dòng tiếp theo có thể thực thi một lần nữa
  • if (x)thực thi printchỉ khi xkhông 0(vì vậy thư mục không tồn tại)

Tuy nhiên, tôi không khuyên bạn nên làm theo cách này, vì nó rất chậm và vụng về. Nhưng công thức này là an toàn , do đó đầu vào không thường xuyên không thể gây hại. (Không chắc là /etc/passwdcó chứa dữ liệu có hại, nhưng có lẽ ai đó muốn sử dụng dữ liệu đó từ một nguồn không đáng tin cậy).

Nếu bạn không thể sử dụng gawk, điều này thật đáng tiếc. Trong trường hợp đó bạn không có |&. Bình thường awkchỉ có thể làm một trong ba điều sau đây:

  • print "data" | cmd; close(cmd): Truyền dữ liệu vào lệnh
  • getline data < cmd; close(cmd): Đọc dữ liệu từ một lệnh
  • ret = system(cmd): Nhận mã trả về của lệnh

"Bình thường" awkchỉ đơn giản là không thể chuyển dữ liệu thành tập lệnh và lấy lại thứ gì đó từ nó một lần nữa (ít nhất là tôi không tìm được cách nào cho nó), vì vậy bạn cần một số tệp trung gian (tempfile) thậm chí còn vụng về hơn.

Quan sát thú vị là, một nhiệm vụ dễ dàng như vậy cũng có thể được thực hiện với vỏ:

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

Bạn không cần bash, mỗi Bourne-shell bình thường có thể thực thi mã trên.

Lưu ý rằng mã shell ở trên phức tạp hơn một chút so với thực sự cần thiết, nhưng đây sẽ là một con trỏ về cách thực sự làm việc với nó (đối với những người chưa có kinh nghiệm đầy đủ về cách hoạt động của shell).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.