¿Cómo hacer grep para contenido después de patrón?

81

Dado un archivo, por ejemplo:

potato: 1234
apple: 5678
potato: 5432
grape: 4567
banana: 5432
sushi: 56789

Me gustaría hacer grep para todas las líneas que comienzan con potato:pero solo canalizar los números que siguen potato:. Entonces, en el ejemplo anterior, la salida sería:

1234
5432

¿Cómo puedo hacer eso?

Léxico
fuente

Respuestas:

113
grep 'potato:' file.txt | sed 's/^.*: //'

grepbusca cualquier línea que contenga la cadena potato:, luego, para cada una de estas líneas, sedreemplaza ( s///- sustituye) cualquier carácter ( .*) desde el principio de la línea ( ^) hasta la última aparición de la secuencia :(dos puntos seguidos de un espacio) con el vacío cadena ( s/...//- sustituya la primera parte por la segunda, que está vacía).

o

grep 'potato:' file.txt | cut -d\   -f2

Para cada línea que contiene potato:, cutdividirá la línea en varios campos delimitados por espacio ( -d\- d= delimitador, \= carácter de espacio de escape, algo como -d" "también habría funcionado) e imprimirá el segundo campo de cada una de esas líneas ( -f2).

o

grep 'potato:' file.txt | awk '{print $2}'

Para cada línea que contenga potato:, awkimprimirá el segundo campo ( print $2) que está delimitado por defecto por espacios.

o

grep 'potato:' file.txt | perl -e 'for(<>){s/^.*: //;print}'

Todas las líneas que contienen potato:se envían a un script de Perl en línea ( -e) que toma todas las líneas de , luego, para cada una de estas líneas, hace la misma sustitución que en el primer ejemplo anterior y luego las imprime.stdin

o

awk '{if(/potato:/) print $2}' < file.txt

El archivo se envía vía stdin( < file.txtenvía el contenido del archivo vía stdinal comando de la izquierda) a un awkscript que, para cada línea que contiene potato:( if(/potato:/)devuelve verdadero si la expresión regular /potato:/coincide con la línea actual), imprime el segundo campo, como se describe encima.

o

perl -e 'for(<>){/potato:/ && s/^.*: // && print}' < file.txt

El archivo se envía mediante stdin( < file.txt, ver arriba) a un script Perl que funciona de manera similar al anterior, pero esta vez también se asegura de que cada línea contenga la cadena potato:( /potato:/es una expresión regular que coincide si la línea actual contiene potato:, y, si lo hace ( &&), luego procede a aplicar la expresión regular descrita anteriormente e imprime el resultado).

eliminar
fuente
3
No se necesitan dos procesos y una tubería. Yo iría por awk '$1 ~ /potato/ { print $2 }' file.txt.
Musiphil
2
El awk sería más idiomáticamenteawk '/potato:/ {print $2}'
Benjamin W.
Los scripts de Perl podrían beneficiarse deperl -pe
tripleee
60

O use afirmaciones de expresiones regulares: grep -oP '(?<=potato: ).*' file.txt

mohit6up
fuente
4
Probé algunas frases ingeniosas de la respuesta aceptada anterior, pero creo que esta respuesta resuelve la pregunta con mayor precisión.
Jake88
3
Alguna explicación: Opción -osignifica imprimir solo la parte correspondiente de la línea. Considerando que -Pinfiere una expresión regular compatible con Perl, que resulta ser una expresión regular de búsqueda posterior positiva(?<=string) .
Serge Stroobandt
9
sed -n 's/^potato:[[:space:]]*//p' file.txt

Uno puede pensar en Grep como un Sed restringido, o en Sed como un Grep generalizado. En este caso, Sed es una herramienta buena y liviana que hace lo que quiere, aunque, por supuesto, también existen otras formas razonables de hacerlo.

thb
fuente
2

Esto imprimirá todo después de cada partido, solo en la misma línea:

perl -lne 'print $1 if /^potato:\s*(.*)/' file.txt

Esto hará lo mismo, excepto que también imprimirá todas las líneas siguientes:

perl -lne 'if ($found){print} elsif (/^potato:\s*(.*)/){print $1; $found++}' file.txt

Se utilizan estas opciones de la línea de comandos:

  • -n bucle alrededor de cada línea del archivo de entrada
  • -l elimina las nuevas líneas antes del procesamiento y las vuelve a agregar después
  • -e ejecutar el código perl
Chris Koknat
fuente
2
grep -Po 'potato:\s\K.*' file

-P usar expresiones regulares de Perl

-o para generar solo el partido

\s para que coincida con el espacio después potato:

\K omitir el partido

.* para que coincida con el resto de la (s) cadena (s)

tuxutku
fuente
1

Puede usar grep, como dicen las otras respuestas. Pero no necesita grep, awk, sed, perl, cut ni ninguna herramienta externa. Puedes hacerlo con puro bash.

Pruebe esto (los puntos y comas están ahí para permitirle ponerlo todo en una línea):

$ while read line;
  do
    if [[ "${line%%:\ *}" == "potato" ]];
    then
      echo ${line##*:\ };
    fi;
  done< file.txt

## le dice a bash que elimine la coincidencia más larga de ":" en $ line desde el frente.

$ while read line; do echo ${line##*:\ }; done< file.txt
1234
5678
5432
4567
5432
56789

o si desea la clave en lugar del valor, %% le dice a bash que elimine la coincidencia más larga de ":" en $ línea desde el final.

$ while read line; do echo ${line%%:\ *}; done< file.txt
potato
apple
potato
grape
banana
sushi

La subcadena para dividir es ": \" porque el carácter de espacio debe escaparse con la barra invertida.

Puede encontrar más como estos en el proyecto de documentación de Linux .

mayordomo
fuente
while reades extremadamente lento; usar una utilidad externa será mucho más rápido siempre que elija una con E / S con búfer (es decir, prácticamente cualquiera de los mencionados en esta respuesta y muchos otros).
tripleee
Además, debe usarlo a read -rmenos que necesite de manera muy específica algún comportamiento heredado bastante molesto antes de POSIX.
tripleee
0

BASH moderno tiene soporte para expresiones regulares:

while read -r line; do
  if [[ $line =~ ^potato:\ ([0-9]+) ]]; then
    echo "${BASH_REMATCH[1]}"
  fi
done
ceving
fuente