Як зробити "якщо не справжню умову"?


317

Я хотів би, щоб echoкоманда виконувалася, коли cat /etc/passwd | grep "sysa"це неправда.

Що я роблю неправильно?

if ! [ $(cat /etc/passwd | grep "sysa") ]; then
        echo "ERROR - The user sysa could not be looked up"
        exit 2
fi

7
Чи !не повинно бути всередині дужок? тобто[ ! EXPR ]
acraig5075

7
@ acraig5075 це дійсно в будь-якому випадку, але в цій заяві взагалі немає потреби в тестовій команді (якою є дужки).
Чарльз Даффі

Відповіді:


454

спробуйте

if ! grep -q sysa /etc/passwd ; then

grepповертається, trueякщо знаходить пошукову ціль, а falseякщо її немає.

Так НЕ false== true.

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

Крім того, дивлячись на свій код таким, який ви маєте, $( ... )слід похвалитись використанням форми cmd-substitution, але подумайте про те, що виходить із процесу. Спробуйте echo $(cat /etc/passwd | grep "sysa")зрозуміти, що я маю на увазі. Ви можете взяти це далі, скориставшись -c(count) опцією, щоб виграти, а потім зробити, if ! [ $(grep -c "sysa" /etc/passwd) -eq 0 ] ; thenщо працює, але це досить стара школа.

Але, ви можете використовувати новітні функції оболонки (арифметичне оцінювання), як

if ! (( $(grep -c "sysa" /etc/passwd) == 0 )) ; then ...`

що також дає вам перевагу використання операторів порівняння на основі c-lang, ==,<,>,>=,<=,%а може бути й декількох інших.

У цьому випадку арифметичне оцінювання за коментарем Орвелолофіла може бути ще більше сформульовано, як-от

if ! (( $(grep -c "sysa" /etc/passwd) )) ; then ....

АБО

if (( ! $(grep -c "sysa" /etc/passwd) )) ; then ....

Нарешті, є нагорода під назвою Useless Use of Cat (UUOC). :-) Деякі люди будуть стрибати вгору-вниз і плакати готці! Я просто скажу, що grepможе приймати ім'я файлу у його cmd-рядку, тож навіщо викликати додаткові процеси та конструкції труб, коли цього не потрібно? ;-)

Я сподіваюся, що це допомагає.


1
Це все досить нерозумно, від моєї відповіді на набагато важче (запитання) [ stackoverflow.com/a/30400327/912236] grep "^$user:" /etc/passwd був би більш правильним способом пошуку / etc / passwd інцидентно - grep -vкуди -v перетворює пошук, якщо хотів щоб уникнути безладу ||
Орвелофіл

1
так, добре там вирішується проблема найефективніше, і тоді є відповідь на конкретне питання. Я намагався відповісти на конкретне питання. Дякуємо за ваші ідеї. Успіхів усім.
обстріл

1
не підбираючи своїх відповідей, цілком їм сподобався. я просто через це я б кинув належним чином обмежений чек на ім’я користувача, інакше, якщо ОП дійсно шукає на "sys" або колись, він отримає цілком сюрприз. ще один на дорогу? (( $( cat file | grep regex | wc -l ) ? 0 : 1 ))
Орвелофіл

1
Чудово! Чомусь реквіляр "! Grep -qs ..." не працював з / proc / mounts і намагався з'ясувати, чи регулярно скидаючи USB-диск на ядро ​​Raspbian 4.9. Цей зробив цю роботу чудово!
DocWeird

33

Я думаю, що це можна спростити в:

grep sysa /etc/passwd || {
    echo "ERROR - The user sysa could not be looked up"
    exit 2
}

або в одному командному рядку

$ grep sysa /etc/passwd || { echo "ERROR - The user sysa could not be looked up"; exit 2; }


4
Добре, але я вважаю за краще відповідь містера Шелтера, оскільки це "документально підтверджено", є "читабельнішим" наміром програміста.
0zkr PM

1
Мені подобається ця версія. Що щодо додавання 1>&2в кінці вашого echoдо друку stderr?
Жульєн

2
@ 0zkrPM Але версія оболонки не працює в оболонці Bourne. Ви отримаєте!: not found
переїзд

1
Уникайте перенаправлення виводу при використанні 'grepподібного. -qпригнічує вихід.
tbc0

8

Що я роблю неправильно?

$(...)має значення , а не статус виходу, тому такий підхід є неправильним. Однак у цьому конкретному випадку це дійсно спрацьовує, тому що sysaбуде надруковано, що робить тестовий вислів справдивим. Однак if ! [ $(true) ]; then echo false; fiзавжди надрукується, falseоскільки trueкоманда нічого не пише в stdout (хоча код виходу 0). Ось чому це потрібно перефразовувати if ! grep ...; then.

Альтернативою була б cat /etc/passwd | grep "sysa" || echo error. Змінити: Як Алекс вказав, кіт марно тут : grep "sysa" /etc/passwd || echo error.

Інші відповіді знайшли досить заплутаними, сподіваюся, що це комусь допоможе.


1

У системах Unix, які його підтримують (не на macOS, здається):

if getent passwd "$username" >/dev/null; then
    printf 'User %s exists\n' "$username"
else
    printf 'User %s does not exist\n' "$username"
fi 

Перевага полягає в тому, що він буде запитувати будь-яку службу каталогів, яка може бути у використанні (YP / NIS або LDAP тощо) та файл бази даних локальних паролів.


Проблема grep -q "$username" /etc/passwdполягає в тому, що він дасть помилковий позитив, коли такого користувача немає, але щось інше відповідає шаблону. Це може статися, якщо десь у файлі є часткова або точна відповідність.

Наприклад, у моєму passwdфайлі є рядковий приказ

build:*:21:21:base and xenocara build:/var/empty:/bin/ksh

Це викликало б правильний матч на таких речах , як caraі enocт.д., навіть якщо немає таких користувачів на моїй системі.

Щоб grepрішення було правильним, вам потрібно буде правильно розібрати /etc/passwdфайл:

if cut -d ':' -f 1 /etc/passwd | grep -qxF "$username"; then
    # found
else
    # not found
fi

... або будь-який інший подібний тест щодо першого з :розділених полів.


@SDsolar Ваш код, ймовірно, не виконується bashв цьому випадку.
Кусалаланда

1

Ось відповідь на прикладі:

Щоб переконатися, що реєстратори даних є в Інтернеті, cronсценарій працює кожні 15 хвилин, який виглядає приблизно так:

#!/bin/bash
#
if ! ping -c 1 SOLAR &>/dev/null
then
  echo "SUBJECT:  SOLAR is not responding to ping" | ssmtp abc@def.com
  echo "SOLAR is not responding to ping" | ssmtp 4151112222@txt.att.com
else
  echo "SOLAR is up"
fi
#
if ! ping -c 1 OUTSIDE &>/dev/null
then
  echo "SUBJECT:  OUTSIDE is not responding to ping" | ssmtp abc@def.com
  echo "OUTSIDE is not responding to ping" | ssmtp 4151112222@txt.att.com
else
  echo "OUTSIDE is up"
fi
#

... і так далі для кожного реєстратора даних, який ви можете побачити в фотомонтажі за адресою http://www.SDsolarBlog.com/montage


FYI, використовуючи &>/dev/nullпереадресацію всього виводу з команди, включаючи помилки, на/dev/null

(Умовний вимагає тільки exit statusвід pingкоманди)

Також FYI зауважте, що оскільки cronзавдання виконуються, так як rootнемає необхідності використовувати sudo pingв cronсценарії.

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