GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie wähle ich mehrere Zeilen aus einer Datei oder aus einer Pipe in einem Skript aus?

Sie können dieses awk verwenden:

awk -v s='2,4' 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' file
two
four

Über ein separates Skript lines.sh :

#!/bin/bash
awk -v s="$1" 'BEGIN{split(s, a, ","); for (i in a) b[a[i]]} NR in b' "$2"

Geben Sie dann Ausführungsberechtigungen:

chmod +x lines.sh

Und nennen Sie es als:

./lines.sh '2,4' 'test.txt'

Versuchen Sie es mit sed :

sed -n '2p; 4p' inputFile

-n sagt sed um die Ausgabe zu unterdrücken, sondern für die Zeilen 2 und 4 , die p (print) Befehl wird verwendet, um diese Zeilen zu drucken.

Sie können auch Bereiche verwenden, z. B.:

sed -n '2,4p' inputFile

Zwei reine Bash-Versionen. Da Sie nach allgemeinen und wiederverwendbaren Lösungen suchen, können Sie sich auch ein wenig Mühe geben. (Siehe auch letzten Abschnitt).

Version 1

Dieses Skript schlürft die gesamte Standardeingabe in ein Array (unter Verwendung von mapfile , also ziemlich effizient) und druckt dann die Zeilen, die in seinen Argumenten angegeben sind. Bereiche sind gültig, z. B.

1-4 # for lines 1, 2, 3 and 4
3-  # for everything from line 3 till the end of the file

Sie können diese durch Leerzeichen oder Kommas trennen. Die Zeilen werden genau in der Reihenfolge ausgegeben, in der die Argumente angegeben werden:

lines 1 1,2,4,1-3,4- 1

wird Zeile 1 zweimal drucken, dann Zeile 2, dann Zeile 4, dann Zeile 1, 2 und 3, dann alles von Zeile 4 bis zum Ende und schließlich wieder Zeile 1.

Bitte schön:

#!/bin/bash

lines=()

# Slurp stdin in array
mapfile -O1 -t lines

# Arguments:
IFS=', ' read -ra args <<< "$*"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
      arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((to=10#${BASH_REMATCH[2]:-$((${#lines[@]}))}))
      ((from==0)) && from=1
      ((to>=${#lines[@]})) && to=${#lines[@]}
      ((from<=to)) || printf >&2 'Argument %d-%d: lines not in increasing order' "$from" "$to"
      for((i=from;i<=to;++i)); do
         printf '%s\n' "${lines[i]}"
      done
   else
      printf >&2 "Error in argument \`%s'.\n" "$arg"
   fi
done
  • Pro:Es ist wirklich cool.
  • Con:Muss den gesamten Stream in den Speicher lesen. Nicht geeignet für unendliche Streams.

Version 2

Diese Version adressiert das frühere Problem der unendlichen Streams. Aber Sie verlieren die Möglichkeit, Zeilen zu wiederholen und neu anzuordnen.

Das Gleiche gilt, Bereiche sind erlaubt:

lines 1 1,4-6 9-

druckt die Zeilen 1, 4, 5, 6, 9 und alles bis zum Ende. Wenn die Zeilenmenge begrenzt ist, wird beendet, sobald die letzte Zeile gelesen wurde.

#!/bin/bash

lines=()
tillend=0
maxline=0

# Process arguments
IFS=', ' read -ra args <<< "[email protected]"

for arg in "${args[@]}"; do
   if [[ $arg = +([[:digit:]]) ]]; then
       arg=$arg-$arg
   fi
   if [[ $arg =~ ([[:digit:]]+)-([[:digit:]]*) ]]; then
      ((from=10#${BASH_REMATCH[1]}))
      ((from==0)) && from=1
      ((tillend && from>=tillend)) && continue
      if [[ -z ${BASH_REMATCH[2]} ]]; then
         tillend=$from
         continue
      fi
      ((to=10#${BASH_REMATCH[2]}))
      if ((from>to)); then
         printf >&2 "Invalid lines order: %s\n" "$arg"
         exit 1
      fi
      ((maxline<to)) && maxline=$to
      for ((i=from;i<=to;++i)); do
         lines[i]=1
      done
   else
      printf >&2 "Invalid argument \`%s'\n" "$arg"
      exit 1
   fi
done

# If nothing to read, exit
((tillend==0 && ${#lines[@]}==0)) && exit

# Now read stdin
linenb=0
while IFS= read -r line; do
   ((++linenb))
   ((tillend==0 && maxline && linenb>maxline)) && exit
   if [[ ${lines[linenb]} ]] || ((tillend && linenb>=tillend)); then
      printf '%s\n' "$line"
   fi
done
  • Pro:Es ist wirklich cool und liest nicht den vollständigen Stream im Speicher.
  • Nachteil:Zeilen können nicht wie in Version 1 wiederholt oder neu angeordnet werden. Geschwindigkeit ist nicht ihre Stärke.

Weitere Gedanken

Wenn Sie wirklich ein großartiges allgemeines Skript wollen, das das tut, was Version 1 und Version 2 tun, und mehr, sollten Sie unbedingt eine andere Sprache verwenden, z. B. Perl:Sie werden viel gewinnen (insbesondere Geschwindigkeit)! Sie werden in der Lage sein, nette Optionen zu haben, die viele viel coolere Sachen machen. Es könnte sich auf lange Sicht lohnen, da Sie ein allgemeines und wiederverwendbares Skript wünschen. Vielleicht haben Sie sogar ein Skript, das E-Mails liest!

Haftungsausschluss. Ich habe diese Skripte nicht gründlich überprüft ... also hüte dich vor Fehlern!


Linux
  1. Zeilen aus Textdatei auswählen, deren IDs in einer anderen Datei aufgeführt sind?

  2. Wie lösche ich mehrere zufällige Zeilen aus einer Textdatei mit Sed?

  3. Wie entferne ich die Zeilen, die in Datei B erscheinen, aus einer anderen Datei A?

  4. Wie unterscheidet sich install -c von cp

  5. Wie kann man eine Datei zeilenweise abschneiden?

So verschieben Sie mehrere Dateitypen gleichzeitig von der Befehlszeile aus

So verbinden Sie mehrere Zeilen in einer Datei in Linux zu einer

So mischen Sie Zeilen in einer Datei unter Linux

So kehren Sie Zeilen in einer Datei zeichenweise in Linux um

So entfernen Sie Zeilen aus einer Datei mit dem Sed-Befehl

So entfernen Sie (^M) Zeichen aus einer Datei in Linux