Как найти позицию персонажа, используя grep?

11

Мне нужно определить положение символа в строке, используя команду grep.

Например, строка RAMSITALSKHMAN|1223333.

grep -n '[^a-zA-Z0-9\$\~\%\#\^]'

Как мне найти позицию |в данной строке?

user82782
источник
это должно быть с grep?
Брайам

Ответы:

29

Вы можете использовать -bдля получения байтового смещения, которое совпадает с позицией для простого текста (но не для UTF-8 или аналогичного).

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|'
14:|

Выше я использую -aпереключатель, чтобы сказать grep использовать ввод как текст; необходимо при работе с двоичными файлами, и -oпереключатель должен выводить только совпадающие символы.

Если вам нужна только позиция, вы можете использовать grep для извлечения только позиции:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' | grep -oE '[0-9]+'
14

Если вы получаете странный вывод, проверьте, включены ли в grep цвета. Вы можете отключить цвета, передав --colors=neverкоманду grep или добавив префикс команды grep с помощью \(который отключит любые псевдонимы), например:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' --color=never | \grep -oE '^[0-9]+'
14

Для строки, которая возвращает несколько совпадений, проследуйте, head -n1чтобы получить первое совпадение.

Обратите внимание, что я использую как в приведенном выше, и обратите внимание, что последний не будет работать, если grep «псевдоним» через исполняемый файл (сценарий или иным образом), только при использовании псевдонимов.

runejuhl
источник
3
Теперь ищем 2;)
Izkata
Спасибо @Izkata, вы правы. Я немного обновил свой пост и добавил недостающую шапку ^:)
runejuhl
1
Какую версию grep вы использовали? Я получаю в 0:|качестве вывода-- потому что 0 это позиция байта начала строки, где |находится.
Алекс
@Alex ГНУ Grep из Debian протяжения: grep (GNU grep) 2.27. Возможно, вы используете OS X?
runejuhl
11

Пытаться:

printf '%s\n' 'RAMSITALSKHMAN|1223333.' | grep -o . | grep -n '|'

вывод:

15:|

Это даст вам позицию с индексом на основе-1.

cuonglm
источник
Его не работает :(
user82782
1
@ user82782: Какую команду вы выполняли? Откуда ты знаешь, что это не сработало?
Cuonglm
printf '%s\n' '|' | grep -o . | grep -n '|'печатает 1не так, 0как ожидалось.
10
1
@ l0b0: ОП не говорит, что хотел индексировать базу 0 или 1.
cuonglm
Я просто имею в виду то, чего ожидает разработчик программного обеспечения.
10
8

Если вы используете оболочку , вы можете использовать чисто встроенные операции без необходимости порождения внешних процессов, таких как или :

$ str="RAMSITALSKHMAN|1223333"
$ tmp="${str%%|*}"
$ if [ "$tmp" != "$str" ]; then
> echo ${#tmp}
> fi
14
$ 

При этом используется расширение параметра, чтобы удалить все вхождения |после любой строки и сохранить его во временной переменной. Тогда нужно просто измерить длину временной переменной, чтобы получить индекс |.

Обратите внимание, что ifпроверяется, |существует ли вообще исходная строка. Если этого не произойдет, тогда временная переменная будет такой же, как и оригинальная.

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

$ echo $((${#tmp}+1))
15
$ 
Цифровая травма
источник
1
Вероятно, лучший ответ, этот синтаксис красивый и очень быстрый и простой в использовании, когда вы понимаете его значение, да здравствует до глубины души
vdegenne
4

Вы можете использовать indexфункцию awk для возврата позиции в символах, где происходит совпадение:

echo "RAMSITALSKHMAN|1223333"|awk 'END{print index($0,"|")}'
15

Если вы не возражаете против использования функции Perl index, это обрабатывает сообщение об отсутствии, одном или нескольких вхождениях символа:

echo "|abc|xyz|123456|zzz|" | \
perl -nle '$pos=-1;while (($off=index($_,"|",$pos))>=0) {print $off;$pos=$off+1}'

Только для удобства чтения конвейер разделен на две строки.

Пока целевой символ найден, indexвозвращает положительное значение, основанное на нуле (0). Следовательно, строка «abc | xyz | 123456 | zzz |» при разборе возвращает позиции 0, 4, 8, 15 и 19.

JRFerguson
источник
для этого использования awk более полезен / прост, чем grep.
Архемар
Это только печатает первую позицию, не будет работать со строкой какRAMSITALSKHMAN|1|223333
cuonglm
3

Мы также можем сделать это, используя «expr match» или «expr index»

expr соответствует $ string $ substring, где $ substring является RE.

echo `expr match "RAMSITALSKHMAN|1223333" '[A-Z]*.|'`

И выше даст вам позицию, потому что он возвращает длину совпадающей подстроки.

Но чтобы быть более конкретным для поиска индекса:

mystring="RAMSITALSKHMAN|122333"
echo `expr index "$mystring" '|'`
bluefoggy
источник
У меня недостаточно репутации для комментирования где-либо еще. Мне лично понравился ответ, данный @Gnouc. Однако зачем использовать awk и делать его сложным, когда мы можем делать простые вещи, используя 'expr'
bluefoggy
@kingsdeb это просто предложение.
Авинаш Радж
@kingsdeb: Поскольку (1) awkрешения могут быть тривиально изменены для сообщения этой информации в каждой строке файла (все, что вам нужно сделать, это удалить то END, что никогда не было действительно необходимым, из ответа JRFerguson, а Avinash Raj уже делает это) ; тогда как, чтобы сделать это с exprрешением, вам нужно было бы добавить явный цикл (а ответ Gnouc совсем не так легко сделать, что я вижу), и (2) awkрешения могут быть адаптированы так, чтобы сообщать обо всех совпадения в каждой строке несколько проще, чем exprрешение (на самом деле, Avinash Raj's тоже это делает).
G-Man говорит: «Восстановите Монику»
Почему вы используете echo `...`здесь?
Стефан Шазелас
Это просто чтобы показать результат здесь
bluefoggy
2

Еще одна команда awk ,

$ echo 'RAMSITALSKHMAN|1223333'| awk 'BEGIN{ FS = "" }{for(i=1;i<=NF;i++){if($i=="|"){print i;}}}'
15

Устанавливая разделитель полей как пустую строку, awk превращает отдельный символ в записи в отдельные поля.

Авинаш Радж
источник
2

некоторые альтернативы включают в себя:

аналогично ответу Гнука, но с оболочкой:

echo 'RAMSITALSKHMAN|1223333' |
tr -c \| \\n | 
sh

sh: line 15: syntax error near unexpected token `|
sh: line 15: `|'

с sedи dcвозможно охватывая несколько строк:

echo 'RAMSITALSKHMAN|1223333' |
sed 's/[^|]/1+/g;s/|/p/;1i0 1+' |dc

15

с $IFS...

IFS=\|; set -f; set -- ${0+RAMSITALSKHMAN|1223333}; echo $((${#1}+1))

Это будет также сказать вам , как много есть , как ...

echo $(($#-1))
mikeserv
источник