Comment puis-je regarder la 17e (ou dernière, si moins) ligne dans les fichiers d'un dossier?

8

J'utilise actuellement

watch head -n 17 *

qui fonctionne, mais montre également toutes les lignes jusqu'au 17. Fondamentalement, je voudrais afficher uniquement la dernière ligne pour chaque fichier qui est affiché avec mon approche actuelle. Comment puis-je y parvenir?

Exemple

Par exemple, réduisons la ligne nr. à 7. Donc:

Exemple de fichier:

1
2
3
4
5
6
7
8

cette ligne:

watch head -n 7 *

les sorties

1
2
3
4
5
6
7

où je veux:

7
Felix Dombek
la source

Réponses:

15

Avec GNU awk:

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

Ce qui donne une sortie comme:

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

Notez que le ./*glob n'est développé qu'une seule fois au moment de l' watchappel.

Your watch head -n 17 *était une vulnérabilité d'injection de commande arbitraire, car son expansion *était en fait interprétée comme du code shell par le shell qui watchinvoque pour interpréter la concaténation de ses arguments avec des espaces.

S'il y avait un fichier appelé $(reboot)dans le répertoire courant, il redémarrerait.

Avec -x, nous disons watchde sauter le shell et d'exécuter directement la commande. Alternativement, vous pouvez faire:

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

Pour watchexécuter un shell qui développerait ce ./*glob à chaque itération. watch foo barest en fait le même que watch -x sh -c 'foo bar'. Lors de l'utilisation watch -x, vous pouvez spécifier le shell que vous voulez et par exemple en choisir un plus puissant comme zshcelui-ci peut faire un globbing récursif et restreindre aux fichiers réguliers:

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

Sans gawk, vous pourriez toujours faire quelque chose comme:

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

Donner une sortie comme:

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

Mais ce serait beaucoup moins efficace car cela implique d'exécuter plusieurs commandes par fichier.

Stéphane Chazelas
la source
Merci pour la note de sécurité. En fait, vous avez raison et ma commande n'a pas fait ce que j'avais besoin de faire, c'est-à-dire regarder les fichiers ajoutés, que votre version résout également. Le seul inconvénient est que j'ai besoin d'apprendre l'AWK, je pensais que cela se ferait avec find . -execou quelque chose comme ça: D
Felix Dombek
11

Combinez headet tailaimez dans ces deux exemples:

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

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

Donc, pour résoudre votre problème réel, la commande est:

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

Remarque sur la 2>/dev/nullpartie, elle est nécessaire car * correspondra aux répertoires et à tous les fichiers que vous pourriez ne pas avoir la permission de lire, ce qui produira un message d'erreur que vous voudrez probablement masquer.

hyde
la source
0

une autre approche avec find, exec et sed

watch find . -type f -name \"*\" -exec sh -c \'\( printf '%-50s ' {} \; sed -n 17p {}\)\' \\\;
dhanlin
la source
1
Ne fonctionne pas pour les fichiers de moins de 17 lignes. Lisez attentivement la question.
Wildcard