it-swarm.com.de

Wie drucke ich bestimmte Spalten nach Namen?

Ich habe die folgende Datei:

id  name  age
1   ed    50
2   joe   70   

Ich möchte nur die Spalten id und age drucken. Im Moment benutze ich nur awk:

cat file.tsv | awk '{ print $1, $3 }'

Dies erfordert jedoch die Kenntnis der Spaltennummern. Gibt es eine Möglichkeit, den Namen der Spalte (in der ersten Zeile angegeben) anstelle der Spaltennummer zu verwenden?

33
Brett Thomas

Vielleicht so etwas:

$ cat t.awk
NR==1 {
    for (i=1; i<=NF; i++) {
        ix[$i] = i
    }
}
NR>1 {
    print $ix[c1], $ix[c2]
}
$ awk -f t.awk c1=id c2=name input 
1 ed
2 joe
$ awk -f t.awk c1=age c2=name input 
50 ed
70 joe

Wenn Sie die zu druckenden Spalten in der Befehlszeile angeben möchten, können Sie Folgendes tun:

$ cat t.awk 
BEGIN {
    split(cols,out,",")
}
NR==1 {
    for (i=1; i<=NF; i++)
        ix[$i] = i
}
NR>1 {
    for (i in out)
        printf "%s%s", $ix[out[i]], OFS
    print ""
}
$ awk -f t.awk -v cols=name,age,id,name,id input 
ed 1 ed 50 1 
joe 2 joe 70 2 

(Beachten Sie das -v Schalter, um die im Block BEGIN definierte Variable abzurufen.)

37
Mat

csvkit

Konvertieren Sie die Eingabedaten in ein CSV-Format und verwenden Sie ein CSV-Tool wie csvcut aus dem csvkit:

$ cat test-cols.dat 
id  name  age
1   ed    50
2   joe   70 

Installieren Sie csvkit:

$ pip install csvkit

Verwenden Sie tr mit der Squeeze-Option -s, Um es in eine gültige CSV-Datei zu konvertieren und csvcut anzuwenden:

$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age
id,age
1,50
2,70

Wenn Sie zum alten Datenformat zurückkehren möchten, können Sie tr ',' ' ' | column -t Verwenden.

$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age | tr ',' ' ' | column -t
id  age
1   50
2   70

Notizen

  • csvkit unterstützt auch verschiedene Trennzeichen ( gemeinsam genutzte Option-d oder --delimiter), gibt jedoch eine CSV-Datei zurück:

    • Wenn die Datei nur Leerzeichen zum Trennen von Spalten verwendet (überhaupt keine Tabulatoren), funktioniert das Folgende

      $ csvcut -d ' ' -S -c 'id,age' test-cols.dat
      id,age
      1,50
      2,70
      
    • Wenn die Datei eine Registerkarte zum Trennen von Spalten verwendet, können die folgenden Arbeiten und csvformat verwendet werden, um die tsv-Datei wiederherzustellen:

      $ csvcut -t -c 'id,age' test-cols.dat | csvformat -T
      id  age
      1   50
      2   70
      

      Soweit ich überprüft habe, ist nur eine einzige Registerkarte zulässig.

  • csvlook kann die Tabelle in einem Markdown-Tabellenformat formatieren

    $ csvcut -t -c "id,age" test-cols.dat | csvlook
    | id | age |
    | -- | --- |
    |  1 |  50 |
    |  2 |  70 |
    
  • OC (Useless Use Of Cat) : Ich mag es so, den Befehl zu konstruieren.

6
Hotschke

Wirf einfach eine Perl-Lösung ins Los:

#!/usr/bin/Perl -wnla

BEGIN {
    @f = ('id', 'age');   # field names to print
    print "@f";           # print field names
}

if ($. == 1) {            # if line number 1
    @n = @F;              #   get all field names
} else {                  # or else
    @v{@n} = @F;          #   map field names to values
    print "@v{@f}";       #   print values based on names
}
5

Wenn Sie diese Felder nur mit Namen anstelle von Zahlen referenzieren möchten, können Sie read verwenden:

while read id name age
do
  echo "$id $age"
done < file.tsv 

BEARBEITEN

Ich habe endlich deine Bedeutung gesehen! Hier ist eine Bash-Funktion, die nur die Spalten druckt, die Sie in der Befehlszeile angegeben haben (von Name).

printColumns () 
{ 
read names
while read $names; do
    for col in $*
    do
        eval "printf '%s ' \$$col"
    done
    echo
done
}

So können Sie es mit der von Ihnen präsentierten Datei verwenden:

$ < file.tsv printColumns id name
1 ed 
2 joe 

(Die Funktion lautet stdin. < file.tsv printColumns ... Entspricht printColumns ... < file.tsv Und cat file.tsv | printColumns ...)

$ < file.tsv printColumns name age
ed 50 
joe 70 

$ < file.tsv printColumns name age id name name name
ed 50 1 ed ed ed 
joe 70 2 joe joe joe

Hinweis: Achten Sie auf die Namen der Spalten, die Sie anfordern! In dieser Version fehlen Sicherheitsüberprüfungen, sodass böse Dinge passieren können, wenn eines der Argumente so etwas wie "anything; rm /my/precious/file" Ist.

4
rozcietrzewiacz

Für was es wert ist. Dies kann eine beliebige Anzahl von Spalten in der Quelle und eine beliebige Anzahl von zu druckenden Spalten in der von Ihnen gewählten Ausgabesequenz verarbeiten. ordne einfach die Argumente neu ...

z.B. Anruf: script-name id age

outseq=([email protected])
colnum=($( 
  for ((i; i<${#outseq[@]}; i++)) ;do 
    head -n 1 file |
     sed -r 's/ +/\n/g' |
      sed -nr "/^${outseq[$i]}$/="
  done ))
tr ' ' '\t' <<<"${outseq[@]}"
sed -nr '1!{s/ +/\t/gp}' file |
  cut -f $(tr ' ' ','<<<"${colnum[@]}") 

ausgabe

id      age
1       50
2       70
3
Peter.O

Wenn die Datei, die Sie lesen, möglicherweise niemals vom Benutzer generiert wird, können Sie den eingebauten Lesevorgang missbrauchen:

f=file.tsv
read $(head -n1 "$f") extra <<<`seq 100`
awk "{print \$$id, \$$age}" "$f"

Die gesamte erste Zeile der Eingabedatei wird in die Argumentliste eingefügt, sodass read alle Feldnamen aus der Kopfzeile als Variablennamen übergeben werden. Dem ersten wird die 1 zugewiesen, die seq 100 erzeugt, der zweite bekommt die 2, der dritte bekommt die 3 und so weiter. Überschüssige seq Ausgabe wird von der Dummy-Variablen extra aufgesaugt. Wenn Sie die Anzahl der Eingabespalten im Voraus kennen, können Sie die 100 entsprechend ändern und extra entfernen.

Das Skript awk ist eine Zeichenfolge in doppelten Anführungszeichen, mit der die durch read definierten Shell-Variablen als awk Feldnummern in das Skript eingesetzt werden können.

2
flabdablet

Normalerweise ist es einfacher, nur den Dateikopf zu betrachten, die Nummer der benötigten Spalte zu zählen (c) und dann Unix cut zu verwenden:

cut -f c -d, file.csv

Aber wenn es viele Spalten oder viele Dateien gibt, verwende ich den folgenden hässlichen Trick:

cut \
  -f $(head -1 file.csv | sed 's/,/\'$'\n/g' | grep -n 'column name' | cut -f1 -d,) \
  -d, \ 
  file.csv

Unter OSX getestet, ist das file.csv wird durch Kommas getrennt.

1
srk

Hier ist eine schnelle Möglichkeit, eine einzelne Spalte auszuwählen.

Angenommen, wir möchten die Spalte "foo":

f=file.csv; colnum=`head -1 ${f} | sed 's/,/\n/g' | nl | grep 'foo$' | cut -f 1 `; cut -d, -f ${colnum} ${f}

Nehmen Sie im Grunde die Kopfzeile, teilen Sie sie in mehrere Zeilen mit einem Spaltennamen pro Zeile auf, nummerieren Sie die Zeilen, wählen Sie die Zeile mit dem gewünschten Namen aus und rufen Sie die zugehörige Zeilennummer ab. Verwenden Sie dann diese Zeilennummer als Spaltennummer für den Befehl cut.

1
jdjensen

Auf der Suche nach einer ähnlichen Lösung (ich benötige die Spalte mit dem Namen id, die möglicherweise eine unterschiedliche Spaltennummer hat), bin ich auf folgende gestoßen:

head -n 1 file.csv | awk -F',' ' {
      for(i=1;i < NF;i++) {
         if($i ~ /id/) { print i }
      }
} '
0
Huib te Pas

awk ist für all seinen Jahrgang von Natur aus ganzzahlig indiziert, ebenso wie cut.

Hier sind einige Tools für die Verarbeitung von namenindizierten Daten (die meisten von ihnen verarbeiten nur CSV und TSV, die sehr beliebte Dateiformate sind):

0
John Kerl

Zu diesem Zweck habe ich ein Python Skript geschrieben), das im Grunde so funktioniert:

with fileinput.input(args.file) as data:
    headers = data.readline().split()
    selectors = [any(string in header for string in args.fixed_strings) or
                 any(re.search(pat, header) for pat in args.python_regexp)
                 for header in headers]

    print(*itertools.compress(headers, selectors))
    for line in data:
        print(*itertools.compress(line.split(), selectors))

Ich habe es hgrep für header grep genannt, es kann so verwendet werden:

$ hgrep data.txt -F foo bar -P ^baz$
$ hgrep -F foo bar -P ^baz$ -- data.txt
$ grep -v spam data.txt | hgrep -F foo bar -P ^baz$

Das gesamte Skript ist etwas länger, da argparse zum Parsen von Befehlszeilenargumenten verwendet wird und der Code wie folgt lautet:

#!/usr/bin/python3

import argparse
import fileinput
import itertools
import re
import sys
import textwrap


def underline(s):
    return '\033[4m{}\033[0m'.format(s)


parser = argparse.ArgumentParser(
    usage='%(prog)s [OPTIONS] {} [FILE]'.format(
        underline('column-specification')),
    description=
        'Print selected columns by specifying patterns to match the headers.',
    epilog=textwrap.dedent('''\
    examples:
      $ %(prog)s data.txt -F foo bar -P ^baz$
      $ %(prog)s -F foo bar -P ^baz$ -- data.txt
      $ grep -v spam data.txt | %(prog)s -F foo bar -P ^baz$
    '''),
    formatter_class=argparse.RawTextHelpFormatter,
)

parser.add_argument(
    '-d', '--debug', action='store_true', help='include debugging information')
parser.add_argument(
    'file', metavar='FILE', nargs='?', default='-',
    help="use %(metavar)s as input, default is '-' for standard input")
spec = parser.add_argument_group(
    'column specification', 'one of these or both must be provided:')
spec.add_argument(
    '-F', '--fixed-strings', metavar='STRING', nargs='*', default=[],
    help='show columns containing %(metavar)s in header\n\n')
spec.add_argument(
    '-P', '--python-regexp', metavar='PATTERN', nargs='*', default=[],
    help='show a column if its header matches any %(metavar)s')

args = parser.parse_args()

if args.debug:
    for k, v in sorted(vars(args).items()):
        print('{}: debug: {:>15}: {}'.format(parser.prog, k, v),
              file=sys.stderr)

if not args.fixed_strings and not args.python_regexp:
    parser.error('no column specifications given')


try:
    with fileinput.input(args.file) as data:
        headers = data.readline().split()
        selectors = [any(string in header for string in args.fixed_strings) or
                     any(re.search(pat, header) for pat in args.python_regexp)
                     for header in headers]

        print(*itertools.compress(headers, selectors))
        for line in data:
            print(*itertools.compress(line.split(), selectors))

except BrokenPipeError:
    sys.exit(1)
except KeyboardInterrupt:
    print()
    sys.exit(1)
0
arekolek

Versuchen Sie dieses kleine awk-Dienstprogramm, um bestimmte Header zu schneiden - https://github.com/rohitprajapati/toyeca-cutter

Anwendungsbeispiel -

awk -f toyeca-cutter.awk -v c="col1, col2, col3, col4" my_file.csv
0
toyeca