我有以下文件:
id name age
1 ed 50
2 joe 70
我只想打印id
和age
列。现在我只用awk
:
cat file.tsv | awk '{ print $1, $3 }'
但是,这需要知道列号。有没有一种方法可以在其中使用列名(在第一行中指定)代替列号?
id
的,而不是$1
和age
替代$3
我有以下文件:
id name age
1 ed 50
2 joe 70
我只想打印id
和age
列。现在我只用awk
:
cat file.tsv | awk '{ print $1, $3 }'
但是,这需要知道列号。有没有一种方法可以在其中使用列名(在第一行中指定)代替列号?
id
的,而不是$1
和age
替代$3
Answers:
也许是这样的:
$ 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
如果要指定要在命令行上打印的列,则可以执行以下操作:
$ 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
开关将获取BEGIN
块中定义的变量。)
awk -f t.awk col1 col2 ... coln input
会很理想;awk -f t.awk cols=col1,col2,...,coln input
也会工作
for (i in out)
没有固有顺序。gawk
提供PROCINFO["sorted_in"]
作为解决方案,用a遍历索引for( ; ; )
可能更好。
只是将Perl解决方案引入其中:
#!/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
}
csvkit
将输入数据转换成CSV格式和使用CSV工具如csvcut
从csvkit
:
$ cat test-cols.dat
id name age
1 ed 50
2 joe 70
安装csvkit:
$ pip install csvkit
tr
与其squeeze选项-s
一起使用,可将其转换为有效的csv文件并应用csvcut
:
$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age
id,age
1,50
2,70
如果要返回旧数据格式,可以使用 tr ',' ' ' | column -t
$ cat test-cols.dat | tr -s ' ' ',' | csvcut -c id,age | tr ',' ' ' | column -t
id age
1 50
2 70
笔记
csvkit还支持不同的定界符(共享选项 -d
或--delimiter
),但返回一个csv文件:
如果文件仅使用空格来分隔列(根本没有制表符),则可以进行以下操作
$ csvcut -d ' ' -S -c 'id,age' test-cols.dat
id,age
1,50
2,70
如果文件使用制表符分隔列,则csvformat
可以进行以下工作,并且可以将其用于获取tsv文件:
$ csvcut -t -c 'id,age' test-cols.dat | csvformat -T
id age
1 50
2 70
据我检查,只允许一个选项卡。
csvlook
可以将表格格式化为降价表格格式
$ csvcut -t -c "id,age" test-cols.dat | csvlook
| id | age |
| -- | --- |
| 1 | 50 |
| 2 | 70 |
UUOC(猫的无用使用):我喜欢这种方式来构造命令。
tr
。直接支持TSV文件,无需将它们转换为CSV。该-t
(又名--tabs
)选项告诉cvscut
使用制表符作为字段分隔符。并且-d
或--delimiter
使用任何字符作为分隔符。
-d
和-t
选项似乎已损坏。它们可以指定输入定界符,但是输出定界符被硬编码为始终是逗号。损坏的IMO-它应该与输入定界符相同,或者具有允许用户设置输出定界符的另一个选项,例如awk
的FS和OFS vars。
如果您只想通过名称而不是数字来引用这些字段,则可以使用read
:
while read id name age
do
echo "$id $age"
done < file.tsv
我终于明白了你的意思!这是一个bash函数,它将仅打印出您在命令行上指定的列(按名称)。
printColumns ()
{
read names
while read $names; do
for col in $*
do
eval "printf '%s ' \$$col"
done
echo
done
}
您可以通过以下方式将其与您提供的文件一起使用:
$ < file.tsv printColumns id name
1 ed
2 joe
(该函数读取stdin
。< file.tsv printColumns ...
等效于printColumns ... < file.tsv
和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
注意:请注意您要求的列的名称!这个版本缺乏健全性检查,因此,如果其中一个参数是"anything; rm /my/precious/file"
id
,name
并且age
,不会改变的事实,为了在您的硬编码read
线。
time { command(s); }
)。
time cat temp.txt | ./col1 CHR POS > /dev/null 99.144u 38.966s 2:19.27 99.1% 0+0k 0+0io 0pf+0w time awk -f col2 c1=CHR c2=POS temp.txt > /dev/null 0.294u 0.127s 0:00.50 82.0% 0+0k 0+0io 0pf+0w
物有所值。它可以按照您选择的输出顺序处理源中任意数量的列以及要打印的任意数量的列。只是重新排列参数...
例如。呼叫:script-name id age
outseq=($@)
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[@]}")
输出
id age
1 50
2 70
如果您正在读取的文件永远无法由用户生成,则可以滥用内置的读取文件:
f=file.tsv
read $(head -n1 "$f") extra <<<`seq 100`
awk "{print \$$id, \$$age}" "$f"
输入文件的整个第一行将替换为参数列表,因此read
会将标题行中的所有字段名称作为变量名称传递。其中第一个分配给seq 100
生成的1,第二个分配给2,第三个获取3,依此类推。多余的seq
输出被伪变量吸收extra
。如果您提前知道输入列的数量,则可以更改100以匹配并摆脱extra
。
该awk
脚本是双引号引起来的字符串,允许将定义的shell变量read
替换为脚本中的awk
字段编号。
为此,我编写了一个Python脚本,其基本工作原理如下:
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))
我把它叫做hgrep
为标题的grep,它可以这样使用:
$ 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$
整个脚本要长一些,因为它用于argparse
解析命令行参数,并且代码如下:
#!/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)
awk
,在其整个年份中,本身都是整数索引, cut
。
以下是一些用于处理名称索引数据的工具(其中大多数仅处理CSV和TSV,它们是非常流行的文件格式):
尝试使用此小型awk实用程序来剪切特定的标头- //github.com/rohitprajapati/toyeca-cutter
用法示例-
awk -f toyeca-cutter.awk -v c="col1, col2, col3, col4" my_file.csv
cat
没必要,顺便说一句 您可以使用awk '{ print $1, $3 }' file.tsv