Linux 使用awk清理csv文件的头
我有一堆csv文件,如下所示:Linux 使用awk清理csv文件的头,linux,bash,shell,csv,awk,Linux,Bash,Shell,Csv,Awk,我有一堆csv文件,如下所示: Time,Channel A (s),(V) 0.00000000,0.58058560 0.00666667,0.58913630 0.01333334,0.58058560 awk 'BEGIN { FS=","; RS="\r\n" } FNR == 1 { x = $1; y = $2 } FNR == 2 { print x " " $1 "," y " " $2 } FNR > 3' 我想将其转换为: Time (s),Channel A
Time,Channel A
(s),(V)
0.00000000,0.58058560
0.00666667,0.58913630
0.01333334,0.58058560
awk 'BEGIN { FS=","; RS="\r\n" } FNR == 1 { x = $1; y = $2 } FNR == 2 { print x " " $1 "," y " " $2 } FNR > 3'
我想将其转换为:
Time (s),Channel A (V)
0.00000000,0.58058560
0.00666667,0.58913630
0.01333334,0.58058560
不同文件的单位不一定相同(即可能存在(ms)、(µV)
等)
<>我用“<代码> AWK”FNR!删除空白行(总是第3行)。3'但是剪切第二行参数并将它们粘贴到第一行字段后面超出了我的能力;(
请帮帮我!
动机:有很多这样的文件。手工操作是不可行的
编辑:
首先:谢谢你帮助我。因为你的两个答案似乎有相同的问题,我的文件中可能有问题?我怀疑空行不知何故会造成麻烦
- shell脚本给出了错误
“预期的空行,读取”
- perl脚本和awk脚本给出了错误的第一行:
(V) (s),Channel A 0.00000000,0.58058560
awk 'BEGIN{
FS=OFS=","
}
FNR==1{
for(i=1;i<=NF;i++) l1[i]=$i
}
FNR==2{
for (i=1;i<=NF;i++) l2[i]=$i
}
FNR==3{
s=""
for (i=1;i in l1 || i in l2; i++)
s=s ? s OFS l1[i] " " l2[i] : s l1[i] " " l2[i]
print s
}
FNR<=3{ next }
1' file
您可以改用perl
perl -lpe 'if($.==1){$x=<>;($T,$C)=$x=~/(\([^\)]\))/g;s/[^,]+\K,.*/ $T$& $C/;<>}' file
Time (s),Channel A (V)
0.00000000,0.58058560
0.00666667,0.58913630
0.01333334,0.58058560
perl-lpe'if($.==1){$x=;($T,$C)=$x=~/(\([^\]\))/g;s/[^,]+\K,./$T$&$C/;}文件
时间,通道A(V)
0.00000000,0.58058560
0.00666667,0.58913630
0.01333334,0.58058560
如果你有CRLF文件,看起来你有
perl -lpe 'chop;if($.==1){$x=<>;($T,$C)=$x=~/(\([^\)]\))/g;s/[^,]+\K,.*/ $T$& $C/;<>}' file
perl-lpe'chop;如果($。==1){$x=;($T,$C)=$x=~/(\([^\])]\)/g;s/[^,]+\K,.*/$T$&$C/;}文件
如果您决定留在bash中,这里有一个脚本将为您执行错误检查
#!/usr/bin/env bash
function Error() { 1>&2 echo "$@"; }
function cleanup_csv() {
IFS=$',\r' read -a Header || { Error "could not read header"; return 1; }
IFS=$',\r' read -a Units || { Error "could not read units"; return 1; }
declare -r NCols=${#Header[@]}
declare -r n_units=${#Units[@]}
[[ $NCols -eq $n_units ]] || {
Error "number of columns in Header ($NCols) not equal to Units ($n_units)"
return 1
}
if IFS=$' \t\r' read -a words; then
if [[ $(echo "${words[*]}") ]]; then
Error "expected empty line, read '${words[*]}'"
return 1
fi
else
Error "could not read line 3, expected empty line"
return 1
fi
local i= sep=
for ((i=0; i<NCols; ++i)); do
printf "%s" "$sep${Header[i]} ${Units[i]}"
sep=,
done
printf "\n"
cat
}
cleanup_csv "$@"
像这样调用
./cleanup_csv.sh < in
我还用两个以上的列对它进行了测试
Time,Channel A,Channel B
(s),(V),(mV)
0.00000000,0.58058560,12.34
0.00666667,0.58913630,3.1415
0.02000002,0.58058560,0.913
这同样有效
Time (s),Channel A (V),Channel B (mV)
0.00000000,0.58058560,12.34
0.00666667,0.58913630,3.1415
0.02000002,0.58058560,0.913
编辑:
我更新了空行的读取和检测,因为您的输入具有Windows样式的行尾(CRLF)。您可以使用od-xc
查看回车。我继续向字符集中添加了制表符以用作字段分隔符(IFS),然后查看echo
之后是否还有任何非空白字符
以下是我测试的输入的十六进制转储(在“空”行中添加了空格和制表符):
简要说明:awk
接受逻辑表达式作为范围模式(可以使用范围内的任何内容),因此FNR==n
意味着该模式适用于当前文件中的行号n;最后一种模式适用于第3行之后的所有内容;省略操作意味着只需将输入打印为已读。因此,第#1行和第#2行根据各自的含义进行特殊处理,而第#3行则被忽略,因为它没有匹配模式
更新:根据@123的建议编辑设置RS
(记录分隔符)。另一种方法是将其包含在脚本本身中,可能与FS
(字段分隔符)一起,如下所示:
Time,Channel A
(s),(V)
0.00000000,0.58058560
0.00666667,0.58913630
0.01333334,0.58058560
awk 'BEGIN { FS=","; RS="\r\n" } FNR == 1 { x = $1; y = $2 } FNR == 2 { print x " " $1 "," y " " $2 } FNR > 3'
想想看,如果是这么多行,那么它可能更适合放在文件中而不是引号中。奇怪的是,这里的问题与@123的答案(perl方式)相同。输出为线路1的通道A
(V)(s)。我的csv文件似乎有问题?我不擅长perl,因此我无法真正解析您的答案,但我得到的结果是第一行的(V)(s),通道a
。第三行被正确删除了,tho.@Paul Probs有回车符,我将添加一个编辑。这看起来是一个很好的bash解决方案,并且很好地说明了为什么移动到一种比在bash中填充内容更好的语言:)确实对我有效,我喜欢用于错误检查的脚本。我将把它作为一个单独的解决方案保存在我的目录中。非常感谢。虽然这段代码可以回答这个问题,但提供关于如何和/或为什么解决问题的额外上下文将提高答案的长期价值。这个解决方案也给了我错误的第一行,我认为这也是由回车引起的(参见perl answer或bash answer)。但我要告诉你们:如果这真的行得通,我会喜欢它,因为我似乎仅仅通过看它就能理解它的作用!;)请您尝试修复它好吗?@Paul只需添加一个标志,将RS
设置为\r\n
,即awk-vRS=“\r\n”-F,'yadayada'文件
@Paul我担心它可能会以同样的方式失败。应该可以修复它——一方面,你可以试试@123的建议;如果失败,您能否发布文本文件前几行的hextump,例如cat file.txt | head-n 5 | hextump-C
明白了,现在应该可以修复了。如其他评论中所述,问题是(DOS样式)行终止。
$ od -xc in.csv
0000000 6954 656d 432c 6168 6e6e 6c65 4120 0a0d
T i m e , C h a n n e l A \r \n
0000020 7328 2c29 5628 0d29 200a 2009 0d20 300a
( s ) , ( V ) \r \n \t \r \n 0
0000040 302e 3030 3030 3030 2c30 2e30 3835 3530
. 0 0 0 0 0 0 0 0 , 0 . 5 8 0 5
0000060 3538 3036 0a0d 2e30 3030 3636 3636 3736
8 5 6 0 \r \n 0 . 0 0 6 6 6 6 6 7
0000100 302c 352e 3938 3331 3336 0d30 300a 302e
, 0 . 5 8 9 1 3 6 3 0 \r \n 0 . 0
0000120 3331 3333 3333 2c34 2e30 3835 3530 3538
1 3 3 3 3 3 4 , 0 . 5 8 0 5 8 5
0000140 3036 0a0d 2e30 3230 3030 3030 3230 302c
6 0 \r \n 0 . 0 2 0 0 0 0 0 2 , 0
0000160 352e 3038 3835 3635 0d30 300a 302e 3632
. 5 8 0 5 8 5 6 0 \r \n 0 . 0 2 6
0000200 3636 3636 2c39 2e30 3835 3139 3633 3033
6 6 6 6 9 , 0 . 5 8 9 1 3 6 3 0
0000220 0a0d 2e30 3330 3333 3333 3633 302c 352e
\r \n 0 . 0 3 3 3 3 3 3 6 , 0 . 5
0000240 3938 3331 3336 0d30 300a 302e 3034 3030
8 9 1 3 6 3 0 \r \n 0 . 0 4 0 0 0
0000260 3030 2c33 2e30 3835 3139 3633 3033 0a0d
0 0 3 , 0 . 5 8 9 1 3 6 3 0 \r \n
0000300
awk -F , -v RS='\r\n' 'FNR == 1 { x = $1; y = $2 } FNR == 2 { print x " " $1 "," y " " $2 } FNR > 3'
awk 'BEGIN { FS=","; RS="\r\n" } FNR == 1 { x = $1; y = $2 } FNR == 2 { print x " " $1 "," y " " $2 } FNR > 3'