说我有以下csv文件:
id,message,time
123,"Sorry, This message
has commas and newlines",2016-03-28T20:26:39
456,"It makes the problem non-trivial",2016-03-28T20:26:41
我想编写一个只返回时间列的bash命令。即。
time
2016-03-28T20:26:39
2016-03-28T20:26:41
最直接的方法是什么?您可以假设标准unix工具的可用性,例如awk,gawk,cut,grep等。
注意是否存在“”转义,以及使用
进行微不足道尝试的换行符cut -d , -f 3 file.csv
徒劳的。
答案 0 :(得分:6)
作为chepner said,我们鼓励您使用能够解析 csv 的编程语言。
这是python中的一个例子:
import csv
with open('a.csv', 'rb') as csvfile:
reader = csv.reader(csvfile, quotechar='"')
for row in reader:
print(row[-1]) # row[-1] gives the last column
答案 1 :(得分:3)
如上所述here
gawk -v RS='"' 'NR % 2 == 0 { gsub(/\n/, "") } { printf("%s%s", $0, RT) }' file.csv \
| awk -F, '{print $NF}'
要使用GNU awk
(对于RT
),要专门处理那些带有双引号字符串的新行,并将那些不在其中的新行放在其外面:
gawk -v RS='"' 'NR % 2 == 0 { gsub(/\n/, "") } { printf("%s%s", $0, RT) }' file
这可以通过沿"
个字符拆分文件并删除其他每个块中的换行符来实现。
输出
time
2016-03-28T20:26:39
2016-03-28T20:26:41
然后使用awk拆分列并显示最后一列
答案 2 :(得分:1)
CSV是一种需要适当解析器的格式(即,不能单独用正则表达式解析)。如果您安装了Python,请使用csv
module而不是普通的BASH。
如果没有,请考虑csvkit,它有很多强大的工具可以从命令行处理CSV文件。
另见:
答案 3 :(得分:1)
在尝试处理lspci -m输出时我碰到了类似的东西,但是嵌入的换行需要首先被转义(尽管IFS =,应该在这里工作,因为它滥用bash"引用评估)。 这是一个例子
f:13.3 "System peripheral" "Intel Corporation" "Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel Target Address Decoder" -r01 "Super Micro Computer Inc" "Device 0838"
我能找到将其带入bash的唯一合理方式是:
# echo 'f:13.3 "System peripheral" "Intel Corporation" "Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel Target Address Decoder" -r01 "Super Micro Computer Inc" "Device 0838"' | { eval array=($(cat)); declare -p array; }
declare -a array='([0]="f:13.3" [1]="System peripheral" [2]="Intel Corporation" [3]="Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Memory Controller 0 - Channel Target Address Decoder" [4]="-r01" [5]="Super Micro Computer Inc" [6]="Device 0838")'
#
不是完整的答案,但可能有帮助!
答案 4 :(得分:0)
sed -e 's/,/\n/g' file.csv | egrep ^201[0-9]-
答案 5 :(得分:0)
使用FS
的另一个awk
替代方案
$ awk -F'"' '!(NF%2){getline remainder;$0=$0 OFS remainder}
NR>1{sub(/,/,"",$NF); print $NF}' file
2016-03-28T20:26:39
2016-03-28T20:26:41
答案 6 :(得分:0)
Vanilla bash 脚本
将此代码保存为parse_csv.sh,赋予其执行权限(chmod +x parse_csv.sh)
#!/bin/bash
# vim: ts=4 sw=4 hidden nowrap
# @copyright Copyright © 2021 Carlos Barcellos <carlosbar at gmail.com>
# @license https://www.gnu.org/licenses/lgpl-3.0.en.html
if [ "$1" = "-h" -o "$1" = "--help" -o "$1" = "-v" ]; then
echo "parse csv 0.1"
echo ""
echo "parse_csv.sh [csv file] [delimiter]"
echo " csv file csv file to parse; default stdin"
echo " delimiter delimiter to use. default is comma"
exit 0
fi
delim=,
if [ $# -ge 1 ]; then
[ -n "$1" ] && file="$1"
[ -n "$2" -a "$2" != "\"" ] && delim="$2"
fi
processLine() {
if [[ ! "$1" =~ \" ]]; then
(
IFSS="$delim" fields=($1)
echo "${fields[@]}"
)
return 0
fi
under_scape=0
fields=()
acc=
for (( x=0; x < ${#1}; x++ )); do
if [ "${1:x:1}" = "${delim:0:1}" -o $((x+1)) -ge ${#1} ] && [ $under_scape -ne 1 ]; then
[ "${1:x:1}" != "${delim:0:1}" ] && acc="${acc}${1:x:1}"
fields+=($acc)
acc=
elif [ "${1:x:1}" = "\"" ]; then
if [ $under_scape -eq 1 ] && [ "${1:x+1:1}" = "\"" ]; then
acc="${acc}${1:x:1}"
else
under_scape=$((!under_scape))
fi
[ $((x+1)) -ge ${#1} ] && fields+=($acc)
else
acc="${acc}${1:x:1}"
fi
done
echo "${fields[@]}"
return 0
}
while read -r line; do
processLine "$line"
done < ${file:-/dev/stdin}
然后使用:parse_csv.sh“csv文件”。要仅打印最后一列,您可以将 echo "${fields[@]}" 更改为 echo "${fields[-1]}"
答案 7 :(得分:0)
Perl 来救援!使用 Text::CSV_XS 模块处理 CSV。
perl -MText::CSV_XS=csv -we 'csv(in => $ARGV[0],
on_in => sub { $_[1] = [ $_[1][-1] ] })
' -- file.csv
csv
子程序处理 csvin
指定输入文件,$ARGV[0]
包含第一个命令行参数,即 file.csv
此处on_in
指定要运行的代码。它获取当前行作为第二个参数,即 $_[1]
。我们只是将整行设置为最后一列的内容。答案 8 :(得分:0)
我觉得你想多了。
$: echo time; grep -Eo '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$' file
time
2016-03-28T20:26:39
2016-03-28T20:26:41
如果你想检查那个逗号只是为了确定,
$: echo time; sed -En '/,[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/{ s/.*,//; p; }' file
time
2016-03-28T20:26:39
2016-03-28T20:26:41
答案 9 :(得分:-1)
awk -F, '!/This/{print $NF}' file
time
2016-03-28T20:26:39
2016-03-28T20:26:41