it-swarm-eu.dev

Jak tisknout určité sloupce podle názvu?

Mám následující soubor:

id  name  age
1   ed    50
2   joe   70   

Chci vytisknout pouze sloupce id a age. Právě teď používám awk:

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

To však vyžaduje znát čísla sloupců. Existuje způsob, jak to udělat, kde mohu použít název sloupce (uvedený v prvním řádku), místo čísla sloupce?

33
Brett Thomas

Možná něco takového:

$ 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

Pokud chcete určit sloupce, které se mají tisknout na příkazovém řádku, můžete udělat něco takového:

$ 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 

(Všimněte si přepínače -v A získejte proměnnou definovanou v bloku BEGIN.)

37
Mat

csvkit

Převeďte vstupní data do formátu CSV a použijte nástroj CSV, jako je csvcut z csvkit:

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

Nainstalovat csvkit:

$ pip install csvkit

Použijte tr s možností mačkání -s Chcete-li jej převést na platný soubor CSV a použít csvcut:

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

Pokud se chcete vrátit do starého formátu dat, můžete použít tr ',' ' ' | column -t

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

Poznámky

  • csvkit podporuje také různé oddělovače ( sdílená volba-d nebo --delimiter), ale vrací soubor csv:

    • Pokud soubor používá k oddělení sloupců pouze mezery (vůbec žádné karty), následující práce

      $ csvcut -d ' ' -S -c 'id,age' test-cols.dat
      id,age
      1,50
      2,70
      
    • Pokud soubor používá kartu k oddělení sloupců, lze k získání souboru tsv použít následující práce: csvformat

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

      Pokud jsem to zkontroloval, je povolena pouze jedna karta.

  • csvlook umí formátovat tabulku ve formátu tabulky dolů

    $ csvcut -t -c "id,age" test-cols.dat | csvlook
    | id | age |
    | -- | --- |
    |  1 |  50 |
    |  2 |  70 |
    
  • OC (Nepoužitelné použití kočky) : Líbí se mi to takto sestavit příkaz.

6
Hotschke

Stačí hodit řešení Perl do položky:

#!/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
Peter John Acklam

Pokud chcete pouze odkazovat na tato pole podle jejich jména místo čísel, můžete použít read:

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

UPRAVIT

Nakonec jsem viděl tvůj význam! Zde je funkce bash, která vytiskne pouze sloupce, které zadáte na příkazovém řádku (podle name).

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

Zde je návod, jak jej použít se souborem, který jste předložili:

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

(Funkce čte stdin. < file.tsv printColumns ... je ekvivalentní printColumns ... < file.tsv a 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

Poznámka: Věnujte pozornost názvům sloupců, které požadujete! Tato verze postrádá hygienické kontroly, takže pokud se jeden z argumentů podobá "anything; rm /my/precious/file"

4
rozcietrzewiacz

Za co to stojí. To může zpracovat libovolný počet sloupců ve zdroji a libovolný počet sloupců pro tisk v libovolné výstupní sekvenci, kterou vyberete; stačí znovu uspořádat args ...

např. volání: 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[@]}") 

výstup

id      age
1       50
2       70
3
Peter.O

Pokud soubor, který čtete nikdy nemohl být vytvořen uživatelem, můžete zneužít vestavěné čtení:

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

Celý první řádek vstupního souboru je nahrazen do seznamu argumentů, takže read je předán všechny názvy polí z řádku záhlaví jako názvy proměnných. První z nich dostane 1, které seq 100 generuje, druhý dostane 2, třetí dostane 3 a tak dále. Nadbytek seq výstup je nasáklý figurínovou proměnnou extra. Pokud znáte počet vstupních sloupců předem, můžete změnit 100 tak, aby odpovídaly a zbavit se extra.

Skript awk je řetězec s dvojitým uvozem, který umožňuje, aby proměnné Shell definované read byly nahrazeny do skriptu jako awk čísla polí.

2
flabdablet

Obvykle je snazší se podívat na záhlaví souboru, spočítat číslo potřebného sloupce (c) a poté použít Unix cut:

cut -f c -d, file.csv

Ale když existuje mnoho sloupců nebo mnoho souborů, používám následující ošklivý trik:

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

Testováno na OSX, file.csv je čárkou.

1
srk

Zde je jeden rychlý způsob výběru jednoho sloupce.

Řekněme, že chceme sloupec s názvem „foo“:

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

V zásadě vezměte řádek záhlaví, rozdělte jej na více řádků s jedním názvem sloupce na řádek, očíslujte řádky, vyberte řádek s požadovaným názvem a načtěte přidružené číslo řádku; pak použijte toto číslo řádku jako číslo sloupce příkazu cut.

1
jdjensen

Při hledání podobného řešení (potřebuji sloupec s názvem id, který může mít proměnné číslo sloupce), narazil jsem na toto:

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

awk je pro všechny své ročníky neodmyslitelně celočíselně indexován, stejně jako cut.

Zde je několik nástrojů navržených pro zpracování dat indexovaných podle jména (většina z nich pracuje pouze s CSV a TSV, což jsou velmi populární formáty souborů):

0
John Kerl

Za tímto účelem jsem napsal skript Python), který v podstatě funguje takto:

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

Nazval jsem to hgrep pro grep záhlaví, lze jej použít takto:

$ 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$

Celý skript je o něco delší, protože používá k analýze argumentů příkazového řádku argparse a kód je následující:

#!/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

Vyzkoušejte tento malý nástroj awk pro snížení konkrétních záhlaví - https://github.com/rohitprajapati/toyeca-cutter

Příklad použití -

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