Holen Sie sich eine bestimmte Zeile aus der Textdatei mit nur Shell-Skript

100

Ich versuche, eine bestimmte Zeile aus einer Textdatei zu erhalten.

Bisher habe ich online nur Dinge wie sed gesehen (ich kann nur sh-not bash oder sed oder ähnliches verwenden). Ich muss dies nur mit einem einfachen Shell-Skript tun.

cat file | while read line
    do
       #do something
    done

Ich weiß, wie man wie oben gezeigt durch Zeilen iteriert, aber was ist, wenn ich nur den Inhalt einer bestimmten Zeile abrufen muss

GangstaGraham
quelle
Kennst du die Zeilennummer?
Mehul Rathod
1
Dann darfst du zählen.
Ignacio Vazquez-Abrams
Ja, die Zeilennummer ist 5 @MehulRathod
GangstaGraham
3
Warum ist catokay, aber sednicht? Das macht keinen Sinn.
William Pursell
5
Weil niemand nein sagen kann cat. Aw ... süß cat!

Antworten:

203

sed:

sed '5!d' file

awk:

awk 'NR==5' file
Kent
quelle
Was ist mit dem Befehl sh? Ich kann sed nicht verwenden, awk. Ich sollte dies in der Frage klarer machen.
GangstaGraham
@GangstaGraham Sie sagten, Sie wissen, wie man durch Linien iteriert, wie wäre es, wenn Sie einen Zähler hinzufügen? Wenn der Zähler Ihre Zielzeilennummer erreicht, holen Sie sich die Leitung und unterbrechen Sie die Schleife. hilft es?
Kent
4
@KanagaveluSugumar las die Infoseite von sed. 5!dbedeutet, alle Zeilen außer 5 zu löschen. Shell var ist möglich, Sie benötigen doppelte Anführungszeichen.
Kent
13
Ich würde vorschlagen, eine weitere Variante hinzuzufügen: sed -n 5pDies scheint für Neulinge logischer zu sein, da -ndies "standardmäßig keine Ausgabe" bedeutet und pfür "Drucken" steht und das Löschen möglicherweise nicht verwirrend erwähnt wird (wenn Leute von Dateien sprechen, neigt das Löschen von Zeilen dazu etwas anderes bedeuten).
Josip Rodin
1
@ JosipRodin Sie haben Recht, -n '5p'funktioniert auch für dieses Problem. Der Unterschied besteht darin, dass 5!dSie hinzufügen können -i, um die Änderung zurück in die Datei zu schreiben. jedoch mit -n 5pIhnen zu haben , sed -n '5p' f > f2&& mv f2 fwieder, für diese Frage, ich bin einverstanden mit Ihrer Meinung.
Kent
21

Angenommen, es linehandelt sich um eine Variable, die Ihre erforderliche Zeilennummer enthält. Wenn Sie headund verwenden können tail, ist dies ganz einfach:

head -n $line file | tail -1

Wenn nicht, sollte dies funktionieren:

x=0
want=5
cat lines | while read line; do
  x=$(( x+1 ))
  if [ $x -eq "$want" ]; then
    echo $line
    break
  fi
done
Mikromosen
quelle
Dieser -eqVergleich gilt für Ganzzahlen, daher wird eine Zeilennummer und kein Zeileninhalt ( $line) benötigt. Dies muss behoben werden, indem z. B. want=5vor der Schleife definiert und dann der -eqVergleich aktiviert wird $want. [bewegt von einer abgelehnten Bearbeitung]
Josip Rodin
1
@JosipRodin Ich habe basierend auf Ihrem Kommentar einen unabhängigen Bearbeitungsvorschlag gemacht, da ich damit einverstanden bin. Hoffentlich wird es diesmal nicht abgelehnt.
Victor Zamanian
15

Sie könnten verwenden sed -n 5p file.

Sie können auch eine Reichweite erhalten, z sed -n 5,10p file.

Nomas Prime
quelle
11

Die beste Leistung Methode

sed '5q;d' file

Weil sednach dem 5. keine Zeilen mehr gelesen werden

Update Experiment von Herrn Roger Dueck

Ich habe wcanadian-insane (6.6MB) installiert und sed -n 1p / usr / share / dict / words und sed '1q; d' / usr / share / dict / words mit dem Befehl time verglichen. Der erste dauerte 0,043 Sekunden, der zweite nur 0,002 Sekunden. Die Verwendung von 'q' ist also definitiv eine Leistungsverbesserung!

Glauben
quelle
1
Dies wird auch allgemein geschrieben:sed -n 5q
William Pursell
3
Ich mag diese Lösung, weil sednach dem 5. keine Zeilen mehr gelesen werden.
Anthony Geoghegan
1
Ich habe wcanadian-insane (6.6MB) installiert sed -n 1p /usr/share/dict/wordsund sed '1q;d' /usr/share/dict/wordsden timeBefehl verglichen und verwendet . Der erste dauerte 0,043 Sekunden, der zweite nur 0,002 Sekunden. Die Verwendung von 'q' ist also definitiv eine Leistungsverbesserung!
Roger Dueck
5

Wenn Sie beispielsweise die Zeilen 10 bis 20 einer Datei abrufen möchten, können Sie jede dieser beiden Methoden verwenden:

head -n 20 york.txt | tail -11

oder

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

p im obigen Befehl steht für Drucken.

Folgendes werden Sie sehen: Geben Sie hier die Bildbeschreibung ein

Mona Jalal
quelle
2

Die Standardmethode für diese Art von Dingen ist die Verwendung externer Tools. Es ist absurd, die Verwendung externer Tools beim Schreiben eines Shell-Skripts nicht zuzulassen. Wenn Sie jedoch keine externen Tools verwenden möchten, können Sie Zeile 5 drucken mit:

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

Beachten Sie, dass dadurch die logische Zeile 5 gedruckt wird. Wenn also input-fileZeilenfortsetzungen enthalten sind, werden diese als einzelne Zeile gezählt. Sie können dieses Verhalten ändern, indem Sie -rdem Lesebefehl hinzufügen . (Welches ist wahrscheinlich das gewünschte Verhalten.)

William Pursell
quelle
1
$((++i))scheint ein Bashismus zu sein; Wenn das OP nur externe Tools verwenden kann, würde ich nicht davon ausgehen, dass sie Zugriff auf mehr als eine Ebene haben/bin/sh
Josip Rodin
@ JosipRodin Nein, es ist eine POSIX- Funktion (die Unterstützung für ++Inkremente ist jedoch ausdrücklich als optional gekennzeichnet).
Tripleee
@tripleee es funktioniert nicht mit modernen Dash als / bin / sh, also würde ich mich nicht darauf verlassen.
Josip Rodin
Aber eine einfache Problemumgehung wie $((i+=1))in Dash funktioniert auch.
Tripleee
$(($i+1))ist die einfache Problemumgehung, an die ich gedacht habe.
Josip Rodin
1

Parallel zur Antwort von William Pursell gibt es hier ein einfaches Konstrukt, das auch in der ursprünglichen v7 Bourne-Shell funktionieren sollte (und somit auch dort, wo Bash nicht verfügbar ist).

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

Beachten Sie auch die Optimierung, breakum aus der Schleife herauszukommen, wenn wir die gesuchte Linie erhalten haben.

Tripleee
quelle
0

Ich mochte keine der Antworten besonders.

Hier ist, wie ich es gemacht habe.

# 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
quelle
-1

Einfach mit Perl! Wenn Sie Zeile 1, 3 und 5 aus einer Datei abrufen möchten, sagen Sie / etc / passwd:

perl -e 'while(<>){if(++$l~~[1,3,5]){print}}' < /etc/passwd
dagelf
quelle
seq 5 | perl -ne 'print if $. ~~ [1, 4, 5]'Aber Smartmatch ist experimentell und es wird von der Verwendung abgeraten
Sorin
Keine der anderen Lösungen ist so prägnant oder ermöglicht so viel Flexibilität. (Warum scheint es, dass alles, was Zeit spart und die Dinge einfacher macht, von "klugen Leuten" "entmutigt" wird, sollen wir alle den ganzen Tag auf Bildschirme starren?)
dagelf
-1
line=5; prep=`grep -ne ^ file.txt | grep -e ^$line:`; echo "${prep#$line:}"
Oder
quelle
3
Können Sie zumindest ein wenig beschreiben, warum diese Arbeit der Person, die die Frage gestellt hat, klarer macht?
ted
Der erste Grep wählt also alle Zeilen aus und fügt zu Beginn Zeilennummern hinzu. Dann wählt der zweite Grep eine bestimmte Zeile aus, indem er mit der Zeilennummer beim Start übereinstimmt. Und schließlich wird die Zeilennummer vom Zeilenstart im Echo abgeschnitten.
Oder
Dies ist sowohl komplex als auch ineffizient im Vergleich zu sed -n 5p, was natürlich immer noch auf so etwas wie optimiert werden kannsed -n '5!d;p;q'
Tripleee