Уникнення символів для точного стану грепу в сценарії Bash


1

У мене є базовий сценарій пошуку конкретних встановлених пакетів в Linux. Якщо не знайдено -> роздрукуйте пакет.

Я звик grep -w, але це не працювало належним чином із -персонажем.

Наприклад,

dpkg -l | grep -w "rpm"

Також знайдемо rpm-common.

Я знайшов рішення для цього за допомогою regex. Проблема в тому, що він не працює в сценарії Bash.

Рішення:

dpkg -l | grep -E '(^|\s)string($|\s)'

Умова:

if [ `dpkg -l | grep -E '(^|\s)$i($|\s)' | wc -l` = 0 ]; then echo$i; fi

$iвідноситься до значення масиву, наприклад arr[index]=$i.

Які якихось символів мені потрібно використовувати, щоб умова працювала?

Відповіді:


1

Тут є дві проблеми: як @AFH вказував, \sце стенограма Perl, а не частина стандартного синтаксису POSIX. Стандартний спосіб представити це з класом символів [[:space:]]. Друга проблема полягає в тому, що змінні посилання не розширюються всередині одиничних лапок; вам потрібно використовувати подвійні лапки (і уникати тієї $частини регулярного вираження).

Крім того, декілька стилістичних рекомендацій: $( )як правило, перевагу над задніми посиланнями для заміни команд (це легше читати, і не має деяких дивних незвичних синтаксичних дивацтв, які мають backticks). Але в цьому випадку ви можете пропустити розширення команди; замість того, щоб використовувати wc -lі порівнювати з нулем, щоб побачити, чи були відповідність, просто використовуйте grepстатус виходу в якості тесту (і -qможливість уберегти його від друку відповідності). Також не забудьте подвоїти посилання на змінні (наприклад, у echoкоманді).

Ось мій рекомендований перепис:

if dpkg -l | grep -Eq "(^|[[:space:]])$i(\$|[[:space:]])"; then echo "$i"; fi

EDIT: як зазначав igor у коментарі, це не працюватиме з іменами пакетів, які містять метасимволи regex (наприклад, g ++ - 5). Можна заздалегідь обробити ім'я пакета, щоб уникнути метахарактерів, але це досить брудно. Але є більш простий спосіб: якщо ви використовуєте bash (а не якусь іншу оболонку), ви можете скористатись базовою функцією rehex bash, щоб виконати відповідність безпосередньо:

if [[ "$(dpkg -l)" =~ (^|[[:space:]])"$i"(\$|[[:space:]]) ]]; then echo "$i"; fi

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


Це рішення не працює для пакетів із спеціальними символами. Наприклад g++-5або g++-4.9. Якісь ідеї?
ігор

@igor Див. редагувати.
Гордон Девіссон

1

Розширені регулярні вирази не підтримують \s, але це Perl REs, тому спробуйте використовувати -Pзамість -E:

dpkg -l | grep -P '(^|\s)rpm($|\s)'

Додаткову інформацію див. У цій посиланні .

grepКерівництво каже , що -Pє експериментальним, але це працює для мене на Ubuntu 16.04.

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