¿Cómo encontrar la posición de un personaje usando grep?


10

Necesito identificar la posición de un personaje en una cadena usando el comando grep.

Ejemplo, la cadena es RAMSITALSKHMAN|1223333.

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

¿Cómo encuentro la posición de |en la cadena dada?


tiene que ser con grep?
Braiam

Respuestas:


28

Puede usar -bpara obtener el desplazamiento de bytes, que es lo mismo que la posición para texto simple (pero no para UTF-8 o similar).

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

En lo anterior, uso el -ainterruptor para decirle a grep que use la entrada como texto; necesario cuando se opera en archivos binarios, y el -ointerruptor para generar solo los caracteres coincidentes.

Si solo desea la posición, puede usar grep para extraer solo la posición:

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

Si obtiene resultados extraños, verifique si grep tiene colores habilitados. Puede deshabilitar los colores pasando --colors=nevera grep, o prefijando el comando grep con un \(que deshabilitará cualquier alias), por ejemplo:

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

Para una cadena que devuelve múltiples coincidencias, canalice head -n1para obtener la primera coincidencia.

Tenga en cuenta que uso ambos en lo anterior, y tenga en cuenta que este último no funcionará si grep está "aliasado" a través de un ejecutable (script u otro), solo cuando use alias.


3
Ahora busque 2;)
Izkata

Gracias @ Izkata, tienes razón. He actualizado un poco mi publicación y agregué el sombrero que falta ^:)
runejuhl

1
¿Qué versión de grep usaste? Obtengo 0:|como salida, porque 0 es la posición de byte del comienzo de la línea donde |se encuentra.
Alex

Grep de GNU @Alex del tramo de Debian: grep (GNU grep) 2.27. ¿Estás quizás usando OS X?
runejuhl

11

Tratar:

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

salida:

15:|

Esto le dará la posición con índice basado-1.


No funciona :(
user82782

1
@ user82782: ¿Qué comando ejecutó? ¿Cómo sabes que no funcionó?
Cuonglm

printf '%s\n' '|' | grep -o . | grep -n '|'impresiones 1, no 0como se esperaba.
l0b0

1
@ l0b0: El OP no dice que quería una base de índice 0 o 1.
Cuonglm

Solo quiero decir lo que esperaría un desarrollador de software.
l0b0

8

Si está utilizando el shell , puede utilizar operaciones puramente integradas sin la necesidad de generar procesos externos como o :

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

Utiliza una expansión de parámetros para eliminar todas las ocurrencias de |seguimientos de cualquier cadena y guardarla en una variable temporal. Es solo una cuestión de medir la longitud de la variable temporal para obtener el índice |.

Tenga en cuenta que ifestá comprobando si |existe en absoluto en la cadena original. Si no es así, la variable temporal será la misma que la original.

Tenga en cuenta también que esto proporciona un índice de base cero |que generalmente es útil cuando se indexan cadenas bash. Sin embargo, si necesita el índice basado en uno, puede hacer esto:

$ echo $((${#tmp}+1))
15
$ 

1
probablemente la mejor respuesta, esta sintaxis es hermosa y tan rápida y fácil de usar cuando comprende su significado, viva hasta el núcleo
vdegenne

4

Puede usar la indexfunción de awk para devolver la posición en los caracteres donde se produce la coincidencia:

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

Si no le importa usar la indexfunción de Perl , esto maneja informar cero, una o más ocurrencias de un personaje:

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

Solo para facilitar la lectura, la tubería se ha dividido en dos líneas.

Siempre que se encuentre el carácter objetivo, indexdevuelve un valor positivo basado en cero (0). Por lo tanto, la cadena "abc | xyz | 123456 | zzz |" cuando se analiza devuelve las posiciones 0, 4, 8, 15 y 19.


para este uso, awk es más útil / fácil que grep.
Archemar

Esto solo imprime la primera posición, no funcionará con cadenas comoRAMSITALSKHMAN|1|223333
cuonglm

3

También podemos hacerlo usando "expr match" o "expr index"

expr match $ string $ substring donde $ substring es un RE.

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

Y arriba le dará la posición porque devuelve la longitud de la subcadena coincidente.

Pero para ser más específico para el índice de búsqueda:

mystring="RAMSITALSKHMAN|122333"
echo `expr index "$mystring" '|'`

No tengo suficiente reputación para comentar en ningún otro lado. Personalmente me gustó la respuesta dada por @Gnouc. Sin embargo, ¿por qué usar awk y hacerlo complejo cuando podemos hacer cosas simples usando 'expr'?
bluefoggy

@kingsdeb es solo una sugerencia.
Avinash Raj

@kingsdeb: Porque (1) las awksoluciones pueden modificarse trivialmente para informar esta información en cada línea de un archivo (todo lo que tiene que hacer es eliminar el END, que nunca fue realmente necesario, de la respuesta de JRFerguson, y Avinash Raj ya lo hace) ; mientras que para hacer eso con la exprsolución, necesitaría agregar un ciclo explícito (y la respuesta de Gnouc no es fácilmente adaptable para hacer eso, eso puedo ver), y (2) las awksoluciones pueden adaptarse para informar todos los coincide en cada línea algo más fácilmente que la exprsolución (de hecho, Avinash Raj's ya lo hace también).
G-Man dice 'Restablecer a Monica' el

¿Por qué usarías echo `...`aquí?
Stéphane Chazelas

Esto es solo para mostrar el resultado aquí
bluefoggy

2

Otro comando awk ,

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

Al establecer el separador de campo como una cadena nula, awk convierte los caracteres individuales en el registro como campos separados.


2

Algunas alternativas incluyen:

similar a la respuesta de Gnouc, pero con el caparazón:

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

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

con sedy dcposiblemente abarcando múltiples líneas:

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

15

con $IFS...

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

Eso también le dirá cómo muchos no son como ...

echo $(($#-1))
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.