Як перевірити, чи існує файл у межах awk? [-d 'ім'я файлу'] не вдалося


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. Статус повернення команди повернення . 

наприклад:

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

Я не міг змусити це працювати з якихось причин, тому мені довелося це зробити: awk '{if (system ("stat" $ 0 "> / dev / null 2> / dev / null") == 1) print $ 0 } '
Олександр Кялл

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, і do-d $ dir, але це дуже допомагає. Тепер мені залишається лише розібратися, як повторити "без результатів", якщо жодного не знайдено. Дякую!
Дуг

2
Зауважте, що awkнасправді це не потрібно; так як ви все одно петлі в оболонці, ви також можете просто зробити це while IFS=: read -r name x uid x x x shell rest ; do ... ; done </etc/passwd.
Яніс

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

@Janis дійсно, хороший пункт, відповідь відредаговано.
тердон

Як зазначає @terdon, [ -d "$6"]насправді синтаксис bash, а не awk. У [виглядає як нормальний синтаксис, але (в одному з Баша / краще weirdnesses для Linux) це фактично синонім testвиконуваної програми (або , можливо , Баш вбудованого версії цього, і, просто щоб бути дивніше, Баш вимагає узгоджень , ]що Байдуже я нічого не знаю, крім того, щоб ввести вас в оману, думаючи, що вся справа справді синтаксис, а не програма). У будь-якому випадку, це не те, про що знає awk. Ось чому вам потрібна system()функція, щоб отримати доступ до неї, посилаючись на неї в контексті bash, де її розуміють
Joe

6

Ви можете скористатися мережевою лінією :

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

1
Це безпечно awk, але, на жаль, не працює для каталогів.
Тіно

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)інформацію про це розширення див. На сторінці чоловіка.

Бігайте з чимось на кшталт:

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 про це. Дякую!
Тіно

3

Це майже диво ...

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

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): Отримайте код повернення команди

"Normal" 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

Вам не потрібно bash, кожна нормальна оболонка Bourne може виконувати вище коду.

Зауважте, що вище код оболонки трохи більш детальний, ніж дійсно потрібно, але це має бути покажчик того, як реально працювати з ним (для людей, які не мають досвіду роботи оболонки).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.