Захват для візерунка на початку або в середині рядка


9

Почну з того, що я думаю, що ця проблема трохи менш невинна, ніж звучить.

Що мені потрібно зробити: перевірити наявність папки в межах змінної середовища PATH. Це може бути на початку або десь після. Мені просто потрібно переконатися, що ця папка там є.

Приклад моєї проблеми - давайте скористаємося /opt/gnome.


SCENARIO 1: папка не знаходиться на початку PATH

# echo "$PATH"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

# echo "$PATH" | grep ":/opt/gnome"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Зауважте, що греп повинен бути достатньо конкретним, щоб він не потрапляв /var/opt/gnome. Звідси і товста кишка.


SCENARIO 2: папка знаходиться на початку PATH.

# echo "$PATH"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

# echo "$PATH" | grep "^/opt/gnome"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Це моя проблема - мені потрібно шукати або двокрапку, або початковий рядок із цією папкою. Що я хотів би зробити, це один з цих двох дужок виразів:

# echo $PATH | grep "[^:]/opt/gnome"
# echo $PATH | grep "[:^]/opt/gnome"

АЛЕ [^і [:мають свої значення. Тому дві вищевказані команди не працюють.

Чи є спосіб, коли я можу схватитися за ці два сценарії в одній команді?


Зверніть увагу , що коментар Жиля на відповідь Костас в ставиться до питання, теж: так як ви не для змісту /opt/gnome:або /opt/gnome$, ви знайдете /opt/gnome-fooабо /opt/gnome/bar.
Скотт

@Scott - Поки ви включаєте в свій матч проміжний простір, ви завжди можете прив’язувати будь-яку струну до голови та хвоста лінії без таких ускладнень. Так самоgrep '^\(any number of other matches:*:\)*my match\(:.*\)*$'
mikeserv

Відповіді:


10

Якщо ви перевіряєте вміст PATHзмінної середовища, а не шукати щось у файлі, то grepце неправильний інструмент. Це легше (і швидше і, мабуть, читабельніше) зробити це в оболонці.

В bash, ksh та zsh:

if [[ :$PATH: = *:/opt/gnome:* ]]; then
 : # already there
else
  PATH=$PATH:/opt/gnome
fi

Портативно:

case :$PATH: in
  *:/opt/gnome:*) :;; # already there
  *) PATH=$PATH:/opt/gnome;;
esac

Зверніть увагу на використання, :$PATH:а не $PATH; таким чином, компонент завжди оточений колонами в рядку пошуку, навіть якщо він був на початку або в кінці $PATH.

Якщо ви шукаєте через рядок файлу, ви можете використовувати розширений регулярний вираз (тобто вимагає grep -E), (^|:)/opt/gnome($|:)щоб відповідати, /opt/gnomeале лише якщо він знаходиться на початку рядка або слідує двокрапці, і лише якщо він знаходиться або в кінці лінії або слідом за двокрапкою.


8

Ви можете використовувати розширені регулярні вирази, просто використовуючи grep -E

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

Відповідає екземпляру на початку:

$ TEST=/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Також відповідає екземпляру в середині:

$ TEST=/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Уникання помилкових позитивних результатів:

$ TEST="/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta"
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"

Ніяких матчів там немає.

Компактний і елегантний. Тестовано на Debian 7.


1
egrepє застарілим використання grep -E(джерело: man grep)
Ентон

Дякую, працює як шарм! Я не вибрав це як відповідь, тому що вважаю, що варіант -w трохи простіший. Ще простіше, ніж я спочатку уявляв!
JamesL

3
Увага. -wВаріант має деякі проблеми. Лише цифри, букви та підкреслення вважаються "словами". Тож деякі незвичні, але можливі ознаки зроблять це невдалим. Приклад echo '/sbin:/usr/sbin:/var-/opt/gnome' | grep -w "/opt/gnome"і echo '/sbin:/usr/sbin:/var./opt/gnome' | grep -w "/opt/gnome". Вони дають неправильні результати.
Луїс Антолін Кано

1
Ви знаходитесь на правильному шляху, але все ще є помилкові спрацьовування: /opt/gnome/somethingelse.
Жил "ТАК - перестань бути злим"

1
Цілком правильно. Ми повинні дбати про кінець явно, а не лише про початок. Я думаю, що це виправляє проблеми echo "/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta" | grep -E "(:|^)/opt/gnome(:|$)". Редагування відповіді.
Луїс Антолін Кано

7

Якщо ви не одружені grep, ви можете використовувати awkта розділяти записи на:

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'

5

Ви також можете використовувати

echo "$PATH" | tr ':' '\n' | grep -x "/opt/gnome"

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


2

Я не знаю, чи достатньо це для відповіді, але

grep -w "/opt/gnome"

задовольнить ваші потреби.

echo '/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome
echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome

але

echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep "/opt/gnome" -o
/opt/gnome
/opt/gnome

Це прекрасно спрацьовує, оскільки колонки - це несловні символи. Дякую!
JamesL

@ Sman865 Є й інша причина: тому що /це не частина слова, але rє.
Костас

2
Увага. Як я сказав у коментарі до своєї відповіді. Існують юридичні символи для імені каталогу, які є несловними символами. Це призводить до неправильних результатів. Зазвичай не закінчувати ім'я каталогу - але це може статися.
Луїс Антолін Кано

4
@ Sman865 помилкових спрацьовувань: /opt/gnome-beta, /home/bob/opt/gnome, ...
Жиля SO- перестати бути злим »

Справа не працює: grep -w /usr/local -o <<< /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games------/usr/local /usr/local /usr/local
пабук

0

Для того, щоб вибрати в /opt/gnomeоточенні символів без слів (нові рядки, :, /і т.д.) , спробуйте це:

grep '\B/opt/gnome'

0

Ви можете зробити це надійно і з невеликими зусиллями grep. Ви можете скористатися розширеннями, які широко доступні і серед яких уже запропоновано багато рішень, але навіть з базовим регулярним виразом це легко зробити, хоча на перший погляд це не може бути інтуїтивно зрозумілим.

З базовим регулярним виразом - і так з grepвами - у вас завжди є два надійних якоря - голова і хвіст лінії. Ви можете прив’язати відповідність обох до них, незалежно від місця їх розташування на лінії, наприклад:

grep '^\(ignore case, delimiter\)*match\(delimiter, ignore case\)*$'

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

І так ви можете зробити, наприклад:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$'

Побачте самі:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$
' <<\INPUT
/opt/gnome-beta
/opt/gnome
/home/bob/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt-gnome-beta
/opt/gnomenot::::/opt/gnome
INPUT

ВИХІД

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome

0

ви помітили крайовий випадок ... ви можете уникнути цього, змусивши виведення: на початку рядка:

 echo ":$PATH" | grep ":/opt/gnome"

або якщо шлях точний, додайте також його в кінці, щоб переконатися, що він обмежений:

 echo ":${PATH}:" | grep ":/opt/gnome:"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.