Commande pour obtenir la nième ligne de STDOUT


210

Existe-t-il une commande bash qui vous permettra d'obtenir la nième ligne de STDOUT?

Autrement dit, quelque chose qui prendrait cette

$ ls -l
-rw-r--r--@ 1 root  wheel my.txt
-rw-r--r--@ 1 root  wheel files.txt
-rw-r--r--@ 1 root  wheel here.txt

et faire quelque chose comme

$ ls -l | magic-command 2
-rw-r--r--@ 1 root  wheel files.txt

Je me rends compte que ce serait une mauvaise pratique lors de l'écriture de scripts destinés à être réutilisés, MAIS en travaillant avec le shell au jour le jour, il serait utile pour moi de pouvoir filtrer mon STDOUT de cette manière.

Je réalise également que ce serait une commande semi-triviale à écrire (tampon STDOUT, retourner une ligne spécifique), mais je veux savoir s'il existe une commande shell standard pour ce faire qui serait disponible sans que je laisse tomber un script en place.


5
Je viens de voter pour l'utilisation du motmagic-command
Sevenearths

Réponses:


332

Utilisation sed, juste pour la variété:

ls -l | sed -n 2p

L'utilisation de cette alternative, qui semble plus efficace car elle arrête de lire l'entrée lorsque la ligne requise est imprimée, peut générer un SIGPIPE dans le processus d'alimentation, ce qui peut générer à son tour un message d'erreur indésirable:

ls -l | sed -n -e '2{p;q}'

Je l'ai vu assez souvent pour que j'utilise habituellement le premier (qui est plus facile à taper, de toute façon), mais ce lsn'est pas une commande qui se plaint quand il obtient SIGPIPE.

Pour une gamme de lignes:

ls -l | sed -n 2,4p

Pour plusieurs gammes de lignes:

ls -l | sed -n -e 2,4p -e 20,30p
ls -l | sed -n -e '2,4p;20,30p'

Que signifie le p? comme 2p 3p? Dans le sens où je comprends que c'est pour 2/3 lignes, mais quelle est la théorie / compréhension derrière cela?
Leo Ufimtsev

4
@LeoUfimtsev: le pest la seddéclaration qui imprime les lignes identifiées par la plage de lignes qui la précède. Cela 2,4psignifie donc imprimer les lignes 2, 3, 4.
Jonathan Leffler

3
Et nsignifie ne PAS imprimer toutes les autres lignes
Timo

85
ls -l | head -2 | tail -1

13
+2, mais je suggère head -n 2 | tail -n 1- les têtes et les queues modernes ont tendance à émettre des avertissements autrement.
Michael Krelin - hacker le

2
L'utilisation de deux processus pour filtrer les données est un peu exagérée (mais, compte tenu de la puissance des machines de nos jours, elles feraient probablement face). La généralisation pour gérer une plage n'est pas tout à fait triviale (pour la plage N..M, vous utilisez head -n M | tail -n M-N+1), et la généralisation pour gérer plusieurs plages n'est pas possible.
Jonathan Leffler

3
la tête passe dans la queue? horrible. Plusieurs meilleures façons de le faire.
camh le

16
Grande chose au sujet * La ligne de commande nix: un million et une façons de faire tout et tout le monde a un favori et un toutes les autres haines façons ... :-)
Beggs

1
Comment imprimer une plage de lignes d'une autre manière: $ ls -l | awk '{if (NR>=4 && NR<=64) print}'(peut ajouter plus de conditions plus facilement, plusieurs plages, etc ...)
user10607

41

Alternative à la belle façon tête / queue:

ls -al | awk 'NR==2'

ou

ls -al | sed -n '2p'

1
le mien est mieux, mais sed est sympa.
Michael Krelin - hacker le

Pas besoin d'un "si" - voir la réponse du pirate (sauf pour la partie sur la recherche de chaque fichier sur le système). ;-)
pause jusqu'à nouvel ordre.

qu'est-ce que cela signifie '2p'?
déchiquetage le

1
@shredding short answer print second line; long -> Lorsqu'il est utilisé, seules les lignes qui correspondent au modèle reçoivent la commande après l'adresse. En bref, lorsqu'elles sont utilisées avec le drapeau / p, les lignes correspondantes sont imprimées deux fois: fichier sed '/ PATTERN / p'. Et bien sûr, le MOTIF est une expression régulière plus
Medhat

Et si 2c'est une variable? Chaque exemple utilise des guillemets simples, mais avec un var, vous avez besoin de guillemets doubles. Pourrions-nous "programmer" awk pour qu'il fonctionne également avec des guillemets simples lors de l'utilisation de var Exemple line =1; ls | awk 'NR==$line'. Je sais que cela bashutilise des guillemets doubles avec vars.
Timo

21

De sed1line :

# print line number 52
sed -n '52p'                 # method 1
sed '52!d'                   # method 2
sed '52q;d'                  # method 3, efficient on large files

De awk1line :

# print line number 52
awk 'NR==52'
awk 'NR==52 {print;exit}'          # more efficient on large files

9

Par souci d'exhaustivité ;-)

code plus court

find / | awk NR==3

durée de vie plus courte

find / | awk 'NR==3 {print $0; exit}'

Vous ne plaisantez pas sur l'exhaustivité!
pause jusqu'à nouvel ordre.

Je ne le suis pas ;-) En fait, je ne sais pas pourquoi tout le monde utilise ls- la question initiale concernait quelqu'un STDOUT, donc j'ai pensé qu'il valait mieux l'avoir plus grand.
Michael Krelin - hacker le

Au moins avec GNU awk, l'action par défaut est { print $0 }, donc `awk 'NR == 3' est un moyen plus court d'écrire la même chose.
éphémère le

Vous devriez probablement ajouter "; exit" à cette action. Inutile de traiter le reste des lignes lorsque vous ne faites rien avec elles.
camh

@camh: Voir la réponse de Jonathan Leffer à ce sujet: "peut générer un SIGPIPE dans le processus d'alimentation, ce qui peut à son tour générer un message d'erreur indésirable".
éphémère


6

Vous pouvez utiliser awk:

ls -l | awk 'NR==2'

Mettre à jour

Le code ci-dessus n'obtiendra pas ce que nous voulons en raison d'une erreur au coup par coup: la première ligne de la commande ls -l est la ligne totale . Pour cela, le code révisé suivant fonctionnera:

ls -l | awk 'NR==3'

4

Perl est-il facilement accessible pour vous?

$ perl -n -e 'if ($. == 7) { print; exit(0); }'

Remplacez évidemment le numéro de votre choix par 7.


Ceci est mon préféré car il semble que ce soit le plus facile à généraliser à ce que j'étais personnellement, ce qui est chaque nième, perl -n -e 'if ($.% 7 == 0) {print; sortie (0); } '
mat kelcey

@matkelcey vous pouvez également faire $ awk 'NR % 7' filenamepour imprimer chaque 7ème ligne de fichier.
user10607

3

Une autre affiche suggérée

ls -l | head -2 | tail -1

mais si vous dirigez la tête dans la queue, il semble que tout jusqu'à la ligne N soit traité deux fois.

Passe la queue dans la tête

ls -l | tail -n +2 | head -n1

serait plus efficace?


0

Oui, le moyen le plus efficace (comme l'a déjà souligné Jonathan Leffler) est d'utiliser sed avec print & quit:

set -o pipefail                        # cf. help set
time -p ls -l | sed -n -e '2{p;q;}'    # only print the second line & quit (on Mac OS X)
echo "$?: ${PIPESTATUS[*]}"            # cf. man bash | less -p 'PIPESTATUS'

L'utilisation de pipefail et de PIPESTATUS est très intéressante et cela ajoute beaucoup à cette page.
Mike S

0

Hmm

sed n'a pas fonctionné dans mon cas. Je propose:

pour les lignes "impaires" 1,3,5,7 ... ls | awk '0 == (NR + 1)% 2'

pour les lignes "paires" 2,4,6,8 ls | awk '0 == (NR)% 2'


-1

Pour plus d'exhaustivité ..

ls -l | (for ((x=0;x<2;x++)) ; do read ; done ; head -n1)

Jetez les lignes jusqu'à ce que vous arriviez à la seconde, puis imprimez la première ligne après cela. Ainsi, il imprime la 3ème ligne.

Si c'est juste la deuxième ligne ..

ls -l | (read; head -n1)

Mettez autant de lectures que nécessaire.


-1

J'ai ajouté ceci dans mon .bash_profile

function gdf(){
  DELETE_FILE_PATH=$(git log --diff-filter=D --summary | grep delete | grep $1 | awk '{print $4}')
  SHA=$(git log --all -- $DELETE_FILE_PATH | grep commit | head -n 1 | awk '{print $2}')
  git show $SHA -- $DELETE_FILE_PATH
}

Et ça marche

gdf <file name>

Je suis perplexe - qu'est-ce que cela a à voir avec la question?
Jonathan Leffler
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.