Nehmen Sie die n-te Spalte in einer Textdatei

84

Ich habe eine Textdatei:

1 Q0 1657 1 19.6117 Exp
1 Q0 1410 2 18.8302 Exp
2 Q0 3078 1 18.6695 Exp
2 Q0 2434 2 14.0508 Exp
2 Q0 3129 3 13.5495 Exp

Ich möchte das 2. und 4. Wort jeder Zeile so nehmen:

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

Ich benutze diesen Code:

 nol=$(cat "/path/of/my/text" | wc -l)
 x=1
 while  [ $x -le "$nol" ]
 do
     line=($(sed -n "$x"p /path/of/my/text)
     echo ""${line[1]}" "${line[3]}""  >> out.txt
     x=$(( $x + 1 ))
 done

Es funktioniert, ist aber sehr kompliziert und die Verarbeitung langer Textdateien dauert lange.

Gibt es einen einfacheren Weg, dies zu tun?

mnrl
quelle
1
2. Wort jeder Zeile einfach 2. Spalte genannt!
Bernard

Antworten:

124

iirc:

cat filename.txt | awk '{ print $2 $4 }'

oder, wie in den Kommentaren erwähnt:

awk '{ print $2 $4 }' filename.txt
Tom van der Woerdt
quelle
15
UUOC !!! awk '{print $2,$4}' filename.txtist besser (keine Pipe, nur ein Programm aufgerufen)
blau
5
@blue Ich verwende oft catin meinen Bash-Skripten, anstatt einen Dateinamen anzugeben, weil der Overhead minimal ist und weil die Syntax cat ... | ... > ...wirklich gut zeigt, was die Eingabe ist und wohin die Ausgabe geht. Sie haben Recht, es wird hier eigentlich nicht benötigt.
Tom van der Woerdt
Vielen Dank! Katze mytext | awk '{print $ 2, $ 3}' funktioniert für mich :) Nochmals vielen Dank.
Mnrl
8
@ TomvanderWoerdt: Ich schreibe manchmal < input awk '{ print $2 $4 }' > outputzu diesem Zweck.
Ruakh
67

Sie können den folgenden cutBefehl verwenden:

cut -d' ' -f3,5 < datafile.txt

druckt

1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495

das

  • -d' '- meine, spaceals Trennzeichen verwenden
  • -f3,5 - 3. und 5. Spalte nehmen und ausdrucken

Das cutist für große Dateien als reine Shell-Lösung viel schneller . Wenn Ihre Datei durch mehrere Leerzeichen begrenzt ist, können Sie diese zuerst entfernen, z. B.:

sed 's/[\t ][\t ]*/ /g' < datafile.txt | cut -d' ' -f3,5

wobei das (gnu) sed ein taboder mehrere spaceZeichen durch ein einzelnes ersetzt space.

Für eine Variante - hier ist auch eine Perl-Lösung:

perl -lanE 'say "$F[2] $F[4]"' < datafile.txt
jm666
quelle
1
Funktioniert gut ... wenn Sie garantiert sind, dass die Anzahl der Leerzeichen in jeder Zeile genau ... :)
Rogerdpack
24

Der Vollständigkeit halber:

while read _ _ one _ two _; do
    echo "$one $two"
done < file.txt

Anstelle _einer beliebigen Variablen (wie junk) kann auch verwendet werden. Es geht nur darum, die Spalten zu extrahieren.

Demo:

$ while read _ _ one _ two _; do echo "$one $two"; done < /tmp/file.txt
1657 19.6117
1410 18.8302
3078 18.6695
2434 14.0508
3129 13.5495
Johannes Weiss
quelle
Ein sehr hübscher Hack!
Nbubis
taaasty - schön!
Olisteadman
Schön, lesbar und keine Perls / Awks / andere erforderlich, alles in einer Shell von Builtins.
Petr Matousu
6

Noch eine einfache Variante -

$ while read line
  do
      set $line          # assigns words in line to positional parameters
      echo "$3 $5"
  done < file
AKA11
quelle
4

Wenn Ihre Datei n Zeilen enthält , muss Ihr Skript die Datei n- mal lesen . Wenn Sie also die Länge der Datei verdoppeln, vervierfachen Sie den Arbeitsaufwand Ihres Skripts - und fast die gesamte Arbeit wird einfach weggeworfen, da Sie nur die Zeilen der Reihe nach durchlaufen möchten.

Der beste Weg, um die Zeilen einer Datei zu durchlaufen, ist die Verwendung einer whileSchleife, wobei der Befehl condition der integrierte Befehl ist read:

while IFS= read -r line ; do
    # $line is a single line of the file, as a single string
    : ... commands that use $line ...
done < input_file.txt

In Ihrem Fall readkönnen Sie schreiben , da Sie die Zeile in ein Array aufteilen möchten und das integrierte System tatsächlich eine spezielle Unterstützung für das Auffüllen einer Array-Variablen bietet. Dies ist das, was Sie möchten:

while read -r -a line ; do
    echo ""${line[1]}" "${line[3]}"" >> out.txt
done < /path/of/my/text

oder noch besser:

while read -r -a line ; do
    echo "${line[1]} ${line[3]}"
done < /path/of/my/text > out.txt

Für das, was Sie tun, können Sie jedoch einfach das cutDienstprogramm verwenden:

cut -d' ' -f2,4 < /path/of/my/text > out.txt

(oder awk, wie Tom van der Woerdt vorschlägt, oder perloder sogar sed).

Ruakh
quelle
würde es vorziehen , readüber , cutweil es gegen mehrere Leerzeichen zwischen den Feldern robust ist und Sie nicht Array Magie benötigen:while read word1 word2 word3 word4 rest; do doSomethingWith $word2 $word4; done
user829755
3

Wenn Sie strukturierte Daten verwenden, hat dies den zusätzlichen Vorteil, dass kein zusätzlicher Shell-Prozess zum Ausführen trund / oder Ausführen aufgerufen wirdcut oder Ähnliches aufgerufen wird. ...

(Natürlich sollten Sie sich mit Bedingungen und vernünftigen Alternativen vor schlechten Eingaben schützen.)

...
while read line ; 
do 
    lineCols=( $line ) ;
    echo "${lineCols[0]}"
    echo "${lineCols[1]}"
done < $myFQFileToRead ; 
...
ingyhere
quelle