¿Cómo imprimir el contenido del archivo solo si la primera línea coincide con cierto patrón?

11

Estoy escribiendo una secuencia de comandos, quiero verificar si la primera línea del archivo coincide con un cierto patrón y, si es así, imprimir el archivo. ¿Cómo puedo conseguir esto?

¿Cómo verifico el patrón? ¿Hay alguna manera de verificar el patrón y, en función de la salida, hacer algo?

EDITAR: Eche un vistazo a esta pregunta: /programming/5536018/how-to-get-match-regex-pattern-using-awk-from-file

Quiero algo como esto, pero ninguno de ellos funcionó para mí. Básicamente quiero verificar si la primera línea coincide con un patrón de expresión regular o no y, en función de eso, imprima las líneas del archivo.

Mateo
fuente
1
¿Cuál es la salida que esperas? ¿Cuál es el patrón que estás buscando? ¿Qué has intentado hasta ahora?
tachomi
@tachomi editado, por favor, eche un vistazo
Mateo

Respuestas:

17

Podrías hacer eso con ed:

ed -s infile <<\IN 2>/dev/null
1s/PATTERN/&/
,p
q
IN

El truco aquí es intentar reemplazar PATTERNen 1stlínea consigo mismo. edproducirá un error si no puede encontrar el patrón especificado, por lo que ,p(imprimir todo el archivo) solo se ejecutará si 1s/PATTERN/&/tiene éxito.

O con sed:

sed -n '1{
/PATTERN/!q
}
p' infile

esto qfunciona si la primera línea no !coincide ( ) PATTERN, de lo contrario pborra todas las líneas.
O, como lo señaló Toby Speight , con GNU sed:

sed '1{/PATTERN/!Q}' infile

Qes lo mismo qpero no imprime el espacio del patrón.

don_crissti
fuente
Podría en Qlugar de qGNU sed, o dantes q(portátil) para no requerir la -nbandera y el pcomando: sed '1{/PATTERN/!Q}' infileo sed -e '1{' -e '/PATTERN/!{' -e 'd' -e 'q' -e '}' -e '}' infile, respectivamente.
Toby Speight
dreinicia el ciclo de comando ¡ Eso siempre me atrapa! : - |
Toby Speight
Con GNU, sedel primer sedcomando se queja sed: -e expression #1, char 10: extra characters after command(debido a p), pero las edúltimas sedsugerencias funcionan bien.
Skippy le Grand Gourou
NB: Las soluciones proporcionadas por esta respuesta tienen el mérito, sobre otras respuestas, de que pueden aplicarse en una tubería.
Skippy le Grand Gourou
1
@SkippyleGrandGourou - trataste de convertirlo en una línea sin separar los comandos con punto y coma - esta es la forma correcta de hacerlosed -n '1{/PATTERN/!q};p'
don_crissti
15

Con el cofre de herramientas POSIX:

{ head -n 1 | grep pattern && cat; } <file
Cuonglm
fuente
1
{doble} <dulce.
mikeserv
@mikeserv: Tengo la intención de usarlo para evitar que una nueva persona se confunda, pero la edición de Stephane es más clara.
Cuonglm
8
 awk '/pattern/{print FILENAME}; {nextfile}' ./*.txt

imprimiría el nombre de los txtarchivos no ocultos en el directorio actual cuya primera línea coincide con la expresión regular extendida patterncon aquellas awkimplementaciones que admitennextfile .

Si en lugar de imprimir el nombre del archivo, desea imprimir todo el contenido del archivo, puede hacer lo siguiente:

 awk 'FNR == 1 && ! /pattern/ {nextfile}; {print}' ./*.txt

Eso es eficiente porque solo ejecuta un comando, pero awkal no ser el comando más eficiente para volcar el contenido de un archivo, con archivos grandes, posiblemente podría obtener mejores rendimientos haciendo algo como:

 awk '/pattern/{printf "%s\0", FILENAME}; {nextfile}' ./*.txt |
   xargs -r0 cat

Es decir, solo se usa awkpara imprimir la lista de archivos que coinciden (delimitados por 0) y se confía en catvolcar su contenido.

Stéphane Chazelas
fuente
6

Si está escribiendo un script de shell, podría hacer algo como

for file in ./*; do head -n 1 "$file" | grep -q 'PATTERN' && cat "$file"; done

O, en Perl:

perl -Tlne '$f = /PATTERN/ if $. == 1; print if $f; $. = 0 if eof' ./*
terdon
fuente
@ Stéphane Chazelas: Quizás close ARGVsea ​​más idiomático que asignar $..
Cuonglm
@terdon Yours parece un código de golf, todo en una línea, sin corchetes alrededor de los nombres de las variables y no fomenta una estructura limpia. Y tenía un signo de dólar perdido cuando publiqué, esa no es la forma de enseñar bash. Supongo que esos factores provienen del trasfondo perl que tú también pareces tener, ¡así que serás perdonado! ;)
@ invitado hola y bienvenido al sitio! Convertí tu respuesta en un comentario ya que las respuestas solo deberían publicarse si están respondiendo la pregunta real. Este no es un foro en el sentido clásico y solo queremos preguntas y respuestas puras aquí. Es posible que desee echar un vistazo al centro de ayuda o hacer un recorrido para comprender mejor el sitio. Dicho esto, mi experiencia es en biología, así que sí, mi código está lejos de ser limpio :) Sin embargo, no veo cómo los corchetes ayudarían aquí, las comillas ya protegen la variable. ¿Qué rompería esto de lo que protegerían los corchetes?
terdon
@ invitado ah, lo siento, olvidé que no puedes comentar. Siéntase libre de venir y explicar en el chat , estoy seguro de que podría aprender algo.
terdon
5

Oldschool, solo traduce tu oración en comandos estándar:

for file in *; do
    if head -n 1 "${file}" | grep -q 'PATTERN'; then
        cat "${file}"
    fi
done

Para aprender bash es un buen comienzo. Si solo necesita una solución rápida, pruebe las respuestas sed, awk o perl. Ambos son agradables, pero son idiomas propios que necesitas (y probablemente quieras) para aprender.

Es un ejemplo bastante simple, por lo que si desea obtener más información, también puede intentar lo mismo en ruby, php, js (por ejemplo, en nodejs) o en cualquier otro idioma que permita el acceso a archivos. Incluso C / C ++ o Java deberían ser fáciles de administrar con una tarea pequeña.

huésped
fuente
1
Esto es básicamente lo mismo que el mío, excepto que se usa en if/elselugar de [ ] &&.
terdon