¿Cómo puedo ver la línea 17 (o la última, si es menos) en los archivos de una carpeta?

8

Actualmente estoy usando

watch head -n 17 *

que funciona, pero también muestra todas las líneas hasta el 17. Básicamente, me gustaría mostrar solo la última línea para cada archivo que se muestra con mi enfoque actual. ¿Cómo puedo lograr eso?

Ejemplo

Por el bien de ejemplo, reduzcamos la línea nr. a 7. Entonces:

Archivo de ejemplo:

1
2
3
4
5
6
7
8

esta línea:

watch head -n 7 *

salidas

1
2
3
4
5
6
7

donde quiero

7
Felix Dombek
fuente

Respuestas:

15

Con GNU awk:

watch -x gawk '
  FNR == 17 {nextfile}
  ENDFILE   {if (FNR) printf "%15s[%02d] %s\n", FILENAME, FNR, $0}' ./*

Lo que da una salida como:

        ./file1[17] line17
  ./short-file2[05] line 5 is the last

Tenga en cuenta que el ./*globo se expande solo una vez cuando watchse invoca.

Tuviste watch head -n 17 *una vulnerabilidad de inyección de comando arbitraria, ya que la expansión de eso en *realidad fue interpretada como código de shell por el shell que watchinvoca para interpretar la concatenación de sus argumentos con espacios.

Si hubiera un archivo llamado $(reboot)en el directorio actual, se reiniciaría.

Con -x, le estamos diciendo watchque omita el shell y ejecute el comando directamente. Alternativamente, puedes hacer:

watch 'exec gawk '\''
  FNR == 17 {nextfile}
  ENDFILE   {if (FNR) printf "%15s[%02d] %s\n", FILENAME, FNR, $0}'\'' ./*'

Para watchejecutar un shell que expandiría ese ./*globo en cada iteración. watch foo baren efecto es lo mismo que watch -x sh -c 'foo bar'. Al usarlo watch -x, puede especificar qué shell desea y, por ejemplo, elegir uno más potente como zshese puede hacer globbing recursivo y restringirlo a archivos normales:

watch -x zsh -c 'awk '\''...'\'' ./**/*(.)'

Sin gawk, aún podrías hacer algo como:

watch '
  for file in ./*; do
    [ -s "$file" ] || continue
    printf "%s: " "$file"
    head -n 17 < "$file" | tail -n 1
  done'

Dando una salida como:

./file1: line17
./short-file2: line 5 is the last

Pero eso sería mucho menos eficiente, ya que implica ejecutar varios comandos por archivo.

Stéphane Chazelas
fuente
Gracias por la nota de seguridad. En realidad tienes razón y mi comando tampoco hizo lo que necesitaba que hiciera, es decir, ver archivos agregados, que tu versión también resuelve. El único inconveniente es que necesito aprender AWK, pensé que esto se haría con find . -execalgo como esto: D
Felix Dombek
11

Combina heady da taillike en estos dos ejemplos:

$ seq 1 80 | head -n 17 | tail -n 1
17

$ seq 1 10 | head -n 17 | tail -n 1
10

Entonces, para resolver su problema real, el comando es:

watch 'for f in *; do head -n 17 -- "$f" 2>/dev/null | tail -n 1 ; done'

Tenga en cuenta la 2>/dev/nullparte, es necesario porque * coincidirá con los directorios y cualquier archivo que no tenga permiso para leer, lo que producirá un mensaje de error, que probablemente quiera ocultar.

Hyde
fuente
0

otro enfoque con find, exec y sed

watch find . -type f -name \"*\" -exec sh -c \'\( printf '%-50s ' {} \; sed -n 17p {}\)\' \\\;
dhanlin
fuente
1
No funciona para archivos con menos de 17 líneas. Lee la pregunta cuidadosamente.
Comodín el