6

Я надеялся на способ заставить sed заменить всю строку заменой (а не просто соответствием), чтобы я мог сделать что-то вроде этого:

sed -e "/$some_complex_regex_with_a_backref/\1/"

и пусть он напечатает только обратную ссылку.

Из этого вопроса, кажется, что способ сделать это - возиться с регулярным выражением, чтобы соответствовать всей строке, или использовать какой-то другой инструмент (например, Perl). Простое изменение regex на .*regex.* Не всегда работает (как упоминалось в этом вопросе). Например:

$ echo $regex
\([:alpha:]*\)day

$ echo $phrase
it is Saturday tomorrow

$ echo $phrase | sed "s/$regex/\1/"
it is Satur tomorrow

$ echo $phrase | sed "s/.*$regex.*/\1/"

$ # what I'd like to have happen
$ echo $phrase | [[[some command or string of commands]]]
Satur

Я ищу самый краткий способ сделать это, предполагая следующее:

  • Регулярное выражение находится в переменной, поэтому не может быть изменено в каждом конкретном случае.
  • Я хотел бы сделать это без использования Perl или других более сложных языков.

4 ответа4

4

Я не знаю достаточно хорошо, чтобы ответить, но если вы гибки и используете grep:

grep --only-matching "complex_regex" file

или же

grep -o "complex_regex" file

Флаг --only-match (или краткая форма -o) указывает grep распечатывать только совпадающую часть, а не всю строку.

0

Ваш первый. * Останавливается на "день", оставляя ваши обратные ссылки пустыми. Вам нужно что-то определенное для сопоставления перед тем, как [[:alpha:]] в вашей обратной ссылке. например, пробел,

$ echo $regex
\([[:alpha:]]*\)day

$ echo $phrase
it is Saturday tomorrow

$ echo $phrase | sed "s/.* $regex.*/\1/"
Satur

Я люблю и ненавижу регулярные выражения.


редактировать:

Расширение слова без границы POSIX (\b), кажется, охватывает оба случая:

$ regex="\b\([[:alpha:]]\+\)day\b"

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

$ cat phrase.txt
it is Saturday tomorrow
it is   Saturday tomorrow
Saturday is the date tomorrow
        Saturday is the date tomorrow
Saturday is the day tomorrow
        Saturday is the day tomorrow
Saturday is the day in dayton tomorrow
        Saturday is the day in dayton tomorrow
Saturday is the day after Friday
The last day of the week is Friday

$ cat phrase.txt | sed -e "s/.*$regex.*/\1/"
Satur
Satur
Satur
Satur
Satur
Satur
Satur
Satur
Satur
Fri

Мне любопытно, если кто-то, у кого есть больше сед-фу, даст лучший ответ. :-)

0

Это близко к ответу mgjk, но с немного другим подходом к сопоставлению границ.

echo $phrase | sed 's/.*[^[:alpha:]]\([[:alpha:]]*\)day.*/\1/'
Satur

Так как .* Будет проглатывать все что угодно, вам сначала нужно сопоставить « не тот символ, который я хочу», а затем «символ, который я хочу». Таким образом, в $regex вы можете хранить

[^[:alpha:]]\([[:alpha:]]*\)day

Он не лишен причуд (не работает в его правильной форме, если "суббота" - первая в строке), но если вы настроены на использование только sed вместо более мощных инструментов, то этого может быть достаточно для вас. Вы также можете сделать это с помощью регулярного выражения, состоящего из двух частей, чтобы решить проблему "начала строки", но затем она снова начинает усложняться, что вам не нужно. Если ваши критерии меняются, существует много решений.

0

Я задал этот вопрос на SO , а также, и получил этот ответ от Potong , что делает то , что я искал.

sed '/'"$regex"'/!b;s//\n\1\n/;s/.*\n\(.*\)\n.*/\1/' file

Обратите внимание, что это не зависит от знания того, что в $regex работает. Он использует переводы строк в качестве значения часового, чтобы впоследствии заменить всю строку только обратной ссылкой.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .