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


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

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

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.


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!


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


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.)


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.


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]}"

-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

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:}"

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.