Come posso guardare la diciassettesima (o ultima, se meno) riga nei file di una cartella?


8

Attualmente sto usando

watch head -n 17 *

che funziona, ma mostra anche tutte le righe fino al 17. Fondamentalmente, vorrei mostrare solo l'ultima riga per ogni file mostrato con il mio approccio attuale. Come posso raggiungerlo?

Esempio

Per esempio, riduciamo la linea nr. a 7. Quindi:

File di esempio:

1
2
3
4
5
6
7
8

questa linea:

watch head -n 7 *

uscite

1
2
3
4
5
6
7

dove voglio:

7

Risposte:


15

Con GNU awk:

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

Che dà un output come:

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

Si noti che il ./*glob viene espanso solo una volta al momento watchviene richiamato.

La tua watch head -n 17 *era una vulnerabilità di iniezione di comando arbitraria in quanto l'espansione di quella *era in realtà interpretata come codice shell dalla shell che watchinvoca per interpretare la concatenazione dei suoi argomenti con spazi.

Se fosse presente un file chiamato $(reboot)nella directory corrente, verrà riavviato.

Con -x, stiamo dicendo watchdi saltare la shell ed eseguire direttamente il comando. In alternativa, potresti fare:

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

Per watcheseguire una shell che espanderebbe quel ./*glob ad ogni iterazione. watch foo barè in effetti lo stesso di watch -x sh -c 'foo bar'. Quando si utilizza watch -x, è possibile specificare quale shell si desidera e, ad esempio, sceglierne una più potente come zshquella può fare globbing ricorsivo e limitarsi ai file regolari:

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

Senza gawk, potresti ancora fare qualcosa del tipo:

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

Dare un'uscita come:

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

Ma sarebbe molto meno efficiente in quanto implica l'esecuzione di più comandi per file.


Grazie per la nota di sicurezza. In realtà hai ragione e anche il mio comando non ha fatto ciò di cui avevo bisogno, ovvero guardare i file aggiunti, che anche la tua versione risolve. L'unico aspetto negativo è che ho bisogno di imparare AWK, avevo pensato che questo sarebbe stato fatto con find . -execo sth in questo modo: D
Felix Dombek,

11

Combina heade metti mi tailpiace in questi due esempi:

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

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

Quindi, per risolvere il tuo vero problema, il comando è:

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

Nota sulla 2>/dev/nullparte, è necessario perché * corrisponderà alle directory e ai file che potresti non avere l'autorizzazione a leggere, il che produrrà un messaggio di errore, che probabilmente vorrai nascondere.


0

un altro approccio con find, exec e sed

watch find . -type f -name \"*\" -exec sh -c \'\( printf '%-50s ' {} \; sed -n 17p {}\)\' \\\;

1
Non funziona per file con meno di 17 righe. Leggi attentamente la domanda.
Wildcard il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.