Obtenez une ligne spécifique à partir d'un fichier texte en utilisant uniquement un script shell

101

J'essaie d'obtenir une ligne spécifique à partir d'un fichier texte.

Jusqu'à présent, en ligne, je n'ai vu que des trucs comme sed, (je ne peux utiliser que sh -not bash ou sed ou quelque chose comme ça). Je dois le faire uniquement en utilisant un script shell de base.

cat file | while read line
    do
       #do something
    done

Je sais comment parcourir les lignes, comme indiqué ci-dessus, mais que faire si j'ai juste besoin d'obtenir le contenu d'une ligne particulière

GangstaGraham
la source
connaissez-vous le numéro de ligne?
Mehul Rathod
1
Ensuite, vous devez compter.
Ignacio Vazquez-Abrams
oui, le numéro de ligne est 5 @MehulRathod
GangstaGraham
3
Pourquoi catça va mais sedpas? Ça n'a aucun sens.
William Pursell
5
Parce que personne ne peut dire non cat. Aw ... mignon cat!

Réponses:

204

sed:

sed '5!d' file

awk:

awk 'NR==5' file
Kent
la source
Et avec la commande sh, je ne peux pas utiliser sed, awk. Je devrais rendre cela plus clair dans la question.
GangstaGraham
@GangstaGraham vous avez dit que vous savez comment parcourir les lignes, que diriez-vous d'ajouter un compteur? si le compteur atteint votre numéro de ligne cible, récupérez la ligne et coupez la boucle. Aide-t-il?
Kent du
4
@KanagaveluSugumar a lu la page d'informations de sed. 5!dsignifie supprimer toutes les lignes sauf 5. shell var est possible, vous avez besoin de guillemets doubles.
Kent
13
Je suggérerais d'ajouter une autre variante: sed -n 5pcela semble plus logique à retenir pour les débutants, car -nsignifie "pas de sortie par défaut" et psignifie "impression", et il n'y a pas de mention potentiellement déroutante de la suppression (lorsque les gens parlent de fichiers, la suppression de lignes a tendance à signifie quelque chose de différent).
Josip Rodin
1
@JosipRodin vous avez raison, -n '5p'fonctionne aussi pour ce problème. La différence ici est que 5!dvous pouvez ajouter -ipour réécrire la modification dans le fichier. cependant, avec -n 5pvous devez sed -n '5p' f > f2&& mv f2 fencore une fois, pour cette question, je suis d'accord avec votre opinion.
Kent
21

En supposant linequ'une variable contient votre numéro de ligne requis, si vous pouvez utiliser headet tail, alors c'est assez simple:

head -n $line file | tail -1

Sinon, cela devrait fonctionner:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done
micromoses
la source
Cette -eqcomparaison concerne les entiers, elle veut donc un numéro de ligne, pas un contenu de ligne ( $line). Cela doit être corrigé en définissant par exemple want=5avant la boucle, puis en utilisant la -eqcomparaison sur $want. [déplacé d'une modification rejetée]
Josip Rodin
1
@JosipRodin J'ai fait une suggestion d'édition indépendante basée sur votre commentaire, car je suis d'accord avec lui. Espérons que cette fois, il ne sera pas rejeté.
Victor Zamanian
16

Vous pourriez utiliser sed -n 5p file.

Vous pouvez également obtenir une gamme, par exemple sed -n 5,10p file.

Nomas Prime
la source
11

Meilleure méthode de performance

sed '5q;d' file

Parce que sedcesse de lire toutes les lignes après la 5e

Mise à jour de l'expérience de M. Roger Dueck

J'ai installé wcanadian-insane (6,6 Mo) et comparé sed -n 1p / usr / share / dict / words et sed '1q; d' / usr / share / dict / words en utilisant la commande time; le premier a pris 0,043 s, le second seulement 0,002 s, donc utiliser «q» est définitivement une amélioration des performances!

foi
la source
1
Ceci est également couramment écrit:sed -n 5q
William Pursell
3
J'aime cette solution car sedarrête de lire les lignes après la 5ème.
Anthony Geoghegan
1
J'ai installé wcanadian-insane (6,6 Mo) et comparé sed -n 1p /usr/share/dict/wordset en sed '1q;d' /usr/share/dict/wordsutilisant la timecommande; le premier a pris 0,043 s, le second seulement 0,002 s, donc utiliser «q» est définitivement une amélioration des performances!
Roger Dueck
5

Si par exemple vous souhaitez obtenir les lignes 10 à 20 d'un fichier, vous pouvez utiliser chacune de ces deux méthodes:

head -n 20 york.txt | tail -11

ou

sed -n '10,20p' york.txt 

p dans la commande ci-dessus représente l'impression.

Voici ce que vous verrez: entrez la description de l'image ici

Mona Jalal
la source
2

La manière standard de faire ce genre de chose est d'utiliser des outils externes. Interdire l'utilisation d'outils externes lors de l'écriture d'un script shell est absurde. Cependant, si vous ne voulez vraiment pas utiliser d'outils externes, vous pouvez imprimer la ligne 5 avec:

i=0; while read line; do test $((++i)) = 5 && echo "$line"; done < input-file

Notez que cela imprimera la ligne logique 5. Autrement dit, si input-filecontient des continuations de ligne, elles seront comptées comme une seule ligne. Vous pouvez modifier ce comportement en ajoutant -rà la commande de lecture. (Ce qui est probablement le comportement souhaité.)

William Pursell
la source
1
$((++i))semble être un bashisme; si l'OP est limité dans l'utilisation d'outils externes, je ne suppose pas qu'ils auront accès à plus qu'une plaine/bin/sh
Josip Rodin
@JosipRodin Non, c'est une fonctionnalité POSIX (mais la prise en charge des ++incréments est spécifiquement marquée comme facultative).
tripleee
@tripleee cela ne fonctionne pas avec le tableau de bord moderne comme / bin / sh, donc je ne m'y fierais pas.
Josip Rodin
Mais une solution de contournement simple, comme celle qui $((i+=1))fonctionne dans Dash, aussi.
tripleee le
$(($i+1))est la solution de contournement simple à laquelle je pensais.
Josip Rodin
1

En parallèle avec la réponse de William Pursell , voici une construction simple qui devrait fonctionner même dans le shell Bourne v7 original (et donc aussi dans les endroits où Bash n'est pas disponible).

i=0
while read line; do
    i=`expr "$i" + 1`
    case $i in 5) echo "$line"; break;; esac
done <file

Remarquez également l'optimisation pour breaksortir de la boucle lorsque nous avons obtenu la ligne recherchée.

tripleee
la source
0

Je n'ai particulièrement aimé aucune des réponses.

Voici comment je l'ai fait.

# Convert the file into an array of strings
lines=(`cat "foo.txt"`)

# Print out the lines via array index
echo "${lines[0]}"
echo "${lines[1]}"
echo "${lines[5]}"
cpp_guy_who_does_gfx
la source
-1

Facile avec perl! Si vous voulez récupérer les lignes 1, 3 et 5 d'un fichier, dites / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
dagelf
la source
seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'mais le smartmatch est expérimental et son utilisation est déconseillée
Sorin
Aucune des autres solutions n'est aussi concise ou n'autorise autant de flexibilité. (Pourquoi semble-t-il que tout ce qui fait gagner du temps et facilite les choses, est "découragé" par les "gens intelligents", sommes-nous tous censés regarder les écrans toute la journée?)
dagelf
-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"
Oder
la source
3
pourriez-vous décrire un peu au moins pourquoi ce travail pour le rendre plus clair à la personne qui a posé la question?
ted
Ainsi, le premier grep sélectionne toutes les lignes en ajoutant les numéros de ligne à leurs débuts. Ensuite, le second grep sélectionne une ligne spécifique en faisant correspondre le numéro de ligne au début. Et enfin le numéro de ligne est coupé à partir du début de ligne en écho.
Oder
C'est à la fois complexe et inefficace par rapport à sed -n 5p, qui, bien sûr, peut toujours être optimisé pour quelque chose commesed -n '5!d;p;q'
tripleee