这是我的输入文件
0164318,001449,001452,001922
0164318,001456,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001224,001860
0841422,001227,001228,001860
我希望我的结果为
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
使用 col1 进行分组并通过 shell 脚本查找 min(col2) 和 max(col3) 。
答案1
使用csvkit
,
$ csvsql -H --query 'SELECT a,min(b),max(c),d FROM file GROUP BY a' file.csv
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
这会将 CSV 数据加载到临时数据库(我相信默认情况下是 SQLite),然后对其应用给定的 SQL 查询。默认情况下,该表与输入文件具有相同的名称(无后缀),并且由于数据缺少列标题,因此默认字段名称将按字母顺序排列。
该-H
选项表明csvsql
数据没有列标题。
要删除输出中生成的标头,请通过类似sed '1d'
.
要获取零填充的整数:
$ csvsql -H --query 'SELECT printf("%07d,%06d,%06d,%06d",a,min(b),max(c),d) FROM file GROUP BY a' file.csv
"printf(""%07d,%06d,%06d,%06d"",a,min(b),max(c),d)"
"0164318,001449,001457,001922"
"0841422,001221,001228,001860"
"0842179,002115,002118,001485"
"0846354,001512,001513,001590"
在这里,这些行被引用,因为我们实际上只为每个结果记录请求一个输出字段(并且它包含逗号)。另一种方法是,需要更多的输入,但不会生成额外的双引号:
$ csvsql -H --query 'SELECT printf("%07d",a),printf("%06d",min(b)),printf("%06d",max(c)),printf("%06d",d) FROM file GROUP BY a' file.csv
"printf(""%07d"",a)","printf(""%06d"",min(b))","printf(""%06d"",max(c))","printf(""%06d"",d)"
0164318,001449,001457,001922
0841422,001221,001228,001860
0842179,002115,002118,001485
0846354,001512,001513,001590
同样,可以通过将结果通过管道来删除输出标头sed '1d'
。
答案2
使用csvkit
:
csvsql -H --query "select a,min(b),max(c),d from file group by a,d" file.csv
请注意,这将截断前导 0。
输出:
a,min(b),max(c),d
164318,1449,1457,1922
841422,1221,1228,1860
842179,2115,2118,1485
846354,1512,1513,1590
答案3
与米勒(http://johnkerl.org/miller/doc), 使用
mlr --ocsv --quote-all --inidx --ifs , cat inputFile | \
mlr --ocsv --quote-none --icsvlite stats1 -g '"1"' -a min,max,min -f '"2","3","4"' \
then cut -f '"1","2"_min,"3"_max,"4"_min' \
then label id,col2,col3,col4 | sed 's/"//g'
你有
id,col2,col3,col4
0164318,001449,001457,001922
0842179,002115,002118,001485
0846354,001512,001513,001590
0841422,001221,001228,001860
答案4
您可以将 SQL 分解为基本的过程操作,并在 shell 脚本中复制它们。
这当然不是一个好主意,因为声明性语言(如 SQL)的优点之一是它们向开发人员隐藏了过程实现的冗长和复杂性,使他们能够专注于数据。 (优化是声明性语言的第二个巨大优势,如果您使用过程程序复制它们,则该优势就会消失)。
另外,这种方法是有问题的,因为在 shell 循环中处理文本通常被认为是不好的做法。
然而,这里是一个 shell 脚本的示例,它利用了许多系统上预安装的标准实用程序(数组构造除外 - POSIX 中未指定,但广泛可用,并且肯定可以使用,因为您正在询问bash
) :
#!/bin/bash
# The input file will be passed as the first argument
file="$1"
# For each input line:
# We take only the values of the first field, sort them, remove duplicates
for i in $(cut -d ',' -f 1 "$file" | sort -n -u); do
# Resetting the array is not really needed; we do it for safety
out=()
# The first field of the output row is the key of the loop
out[0]="$i"
# We only consider the rows whose first field is equal
# to the current key (grep) and...
# ... we sort the values of the second field
# in ascending order and take only the first one
out[1]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 2 | sort -n | head -n 1)"
# ... we sort the values of the third field in
# ascending order and take only the last one
out[2]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 3 | sort -n | tail -n 1)"
# ... we sort the values of the fourth field in
# ascending order and take only the first one
out[3]="$(grep "^${out[0]}" "$file" | cut -d ',' -f 4 | sort -n | head -n 1)"
# Finally we print out the output, separating fields with ','
printf '%s,%s,%s,%s\n' "${out[@]}"
done
它应该被调用为
./script file
该脚本相当于
SELECT col1, MIN(col2), MAX(col3), MIN(col4)
FROM text
GROUP BY col1
ORDER BY col1