Grep для шаблона в начале или в середине строки

9

Начну с того, что считаю эту проблему чуть менее невинной, чем кажется.

Что мне нужно сделать: проверить папку в переменной окружения PATH. Это может быть в начале или где-то после. Мне просто нужно убедиться, что эта папка есть.

Пример моей проблемы - давайте использовать /opt/gnome.


СЦЕНАРИЙ 1: папка не находится в начале пути

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

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

Обратите внимание, что grep должен быть достаточно конкретным, чтобы он не зацепился /var/opt/gnome. Отсюда и двоеточие.


СЦЕНАРИЙ 2: папка находится в начале ПУТИ.

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

НО [^и [:имеют свои значения. Таким образом, две команды выше не работают.

Есть ли способ я могу grep для этих двух сценариев в одной команде?

JamesL
источник
Обратите внимание, что комментарий Жиля к ответу Костаса также относится и к вопросу: поскольку вы не ищите /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$/'
jasonwryan
источник
5

Вы также можете использовать

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

которая разбивает переменную пути на отдельные строки (по одной на путь), поэтому grep -xможно искать точные результаты. Это, конечно, имеет тот недостаток, что требует дополнительного процесса tr. И это не будет работать, когда имя папки PATHсодержит символы новой строки.

TBrandt
источник
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'
jimmij
источник
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
mikeserv
источник
0

вы заметили крайний случай ... вы могли бы избежать этого, вызвав появление: в начале строки:

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

или, если путь точный, добавьте также один в конце, чтобы убедиться, что он ограничен:

 echo ":${PATH}:" | grep ":/opt/gnome:"
Оливье Дюлак
источник