从长行按位置删除字段

时间:2012-03-27 15:21:29

标签: linux bash csv awk

确切地说,我有一个长的分号分隔的字段,其中69个字段。

我需要删除字段3,所以我可以以详细的方式执行:

awk -F\; '$1 == 3 { print $1";"$2";"$4 ... }' a.txt

哪个会变得很长。有没有捷径可以说“4美元到底”,“4美元到69美元”或者只是“删除3美元”?

与问题相关:重复“;”到处都是非常不方便的。

当然,我可以部分生成命令:

echo -e "\b"{4..69}"\";\"$"

虽然它看起来很聪明,但结果却是一个多线命令,处理起来并不优雅。

什么是优雅的解决方案 - 最好是纯粹的awk。

我想我可以快速找到一个sed-solution,但我还有更多的事情要做(重新计算Field 5:如果Field 1 == 2,Field5 = 5-Field5),这在sed中很难,但我想非常适合awk。

我正在使用Gnu-AWK 3.1.6,如果它很重要,但根据apropos有:

  • AWK
  • GAWK
  • igawk
  • mawk
  • NAWK
  • pgawk

好的,更新:

我应该知道的更好,并立即提供一些测试数据,但当然,我会尝试所有的答案,并提出看起来很有希望的东西。

3;03.2012;7228;0;1;3;1;3;4;3;1;3;4;3;2;0;4;4;1;1;4;2;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;3;0;3;1;3;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
3;03.2012;7229;0;2;2;0;5;5;4;4;5;5;4;4;2;5;5;0;0;3;3;0;0;5;6;0;0;0;0;0;2;2;1;2;1;2;2;2;4;3;4;1;5;4;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;4;0;0;0;0;0;0;0;
3;03.2012;7230;0;2;2;2;4;3;4;4;4;3;3;3;2;4;6;1;1;1;6;5;1;6;6;1;1;1;1;1;2;2;1;2;2;0;2;2;3;4;2;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;3;3;4;4;0;0;0;0;0;0;0;
3;03.2012;7231;0;1;3;1;4;4;3;3;4;4;4;4;2;5;5;1;1;4;6;5;1;4;1;1;1;1;1;5;2;1;1;2;0;0;1;2;4;4;3;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;3;0;0;0;0;0;0;0;

抓住这条线。 :)

8 个答案:

答案 0 :(得分:5)

我很抱歉打断这种不正常的高尔夫比赛。你是masochists乐于重新发明轮子?文明为现代人提供污水收集和CSV库等设施,因此他无需处理 -

csvfix怎么样?它是一个命令行工具,可以处理流入和流出的文本,即。和awk一样的环境。您需要的命令是exclude

csvfix exclude -f 3 -rsep ";" a.txt

答案 1 :(得分:3)

一种方式:

awk '{ 
  split( $0, f, /;/ );
  delete f[3];
  for (i=1; i<=length(f); i++) { 
    printf "%s", f[i] ? f[i] ";" : "" 
  } 
}' <<<"one;two;three;four;five;six;seven"

使用以下输出:

one;two;four;five;six;seven;

答案 2 :(得分:2)

您可以改为使用cut命令:

cut -d';' -f1,2,4- a.txt

字段列表可以是一个范围,可以包含一个开放范围(如此处使用的4-)

如果你仍然需要在awk中处理结果,你可以将输出管道输入它。

答案 3 :(得分:1)

您可以使用以下内容:

awk -v fl=<filed_list> 'BEGIN {
  n = split(fl, t, " ")
  for (i = 0; ++i <= n;)
    fa[t[i]]
  }
{
  for (i = 0; ++i <= NF;)
    if (!(i in fa))
      printf "%s", ($i (i < NF ? OFS : ORS))
  }' 

考虑以下输入:

zsh-4.3.14[t]% paste -sd\; < <(printf '%s\n' {1..10})
1;2;3;4;5;6;7;8;9;10

删除第3个字段:

zsh-4.3.14[t]% paste -sd\; < <(printf '%s\n' {1..10}) |
pipe>   awk -F\; -v fl=3 'BEGIN {
pipe quote>     n = split(fl, t, " ")
pipe quote>     for (i = 0; ++i <= n;)
pipe quote>       fa[t[i]]
pipe quote>     }
pipe quote>   {
pipe quote>     for (i = 0; ++i <= NF;)
pipe quote>       if (!(i in fa))
pipe quote>     printf "%s", ($i (i < NF ? OFS : ORS))
pipe quote>   }' OFS=\;
1;2;4;5;6;7;8;9;10

删除一组字段:

zsh-4.3.14[t]% paste -sd\; < <(printf '%s\n' {1..10}) |
pipe>   awk -F\; -v fl='7 4 3' 'BEGIN {
pipe quote>     n = split(fl, t, " ")
pipe quote>     for (i = 0; ++i <= n;)
pipe quote>       fa[t[i]]
pipe quote>     }
pipe quote>   {
pipe quote>     for (i = 0; ++i <= NF;)
pipe quote>       if (!(i in fa))
pipe quote>     printf "%s", ($i (i < NF ? OFS : ORS))
pipe quote>   }' OFS=\;
1;2;5;6;8;9;10

如果您删除最后一个字段(使用或不使用尾随FS),请告诉我输出应该如何。

考虑使用单个字符字段分隔符,对于简单任务, cut 就足够了:

zsh-4.3.14[t]% paste -sd\; < <(printf '%s\n' {1..10}) | cut -d\; -f 1-2,4-
1;2;4;5;6;7;8;9;10
zsh-4.3.14[t]% paste -sd\; < <(printf '%s\n' {1..10}) | cut -d\; -f 1-2,5-6,8-
1;2;5;6;8;9;10

[编辑:点击这里的评论]

给出样本输入:

3;03.2012;7228;0;1;3;1;3;4;3;1;3;4;3;2;0;4;4;1;1;4;2;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;3;0;3;1;3;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
3;03.2012;7229;0;2;2;0;5;5;4;4;5;5;4;4;2;5;5;0;0;3;3;0;0;5;6;0;0;0;0;0;2;2;1;2;1;2;2;2;4;3;4;1;5;4;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;4;0;0;0;0;0;0;0;
3;03.2012;7230;0;2;2;2;4;3;4;4;4;3;3;3;2;4;6;1;1;1;6;5;1;6;6;1;1;1;1;1;2;2;1;2;2;0;2;2;3;4;2;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;3;3;4;4;0;0;0;0;0;0;0;
3;03.2012;7231;0;1;3;1;4;4;3;3;4;4;4;4;2;5;5;1;1;4;6;5;1;4;1;1;1;1;1;5;2;1;1;2;0;0;1;2;4;4;3;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;3;0;0;0;0;0;0;0;

以及以下 awk 脚本:

zsh-4.3.14[t]% cat s.awk 
BEGIN {
  n = split(fl, t, " ")
  for (i = 0; ++i <= n;)
    fa[t[i]]
  }
{
  for (i = 0; ++i <= NF;)
    if (!(i in fa))
      printf "%s", ($i (i < NF ? OFS : ORS))
  } 

使用此命令:

zsh-4.3.14[t]% awk -F\; -v fl=3 -f s.awk OFS=\; infile > outfile

...我得到以下输出:

zsh-4.3.14[t]% cat outfile
3;03.2012;0;1;3;1;3;4;3;1;3;4;3;2;0;4;4;1;1;4;2;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;3;0;3;1;3;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
3;03.2012;0;2;2;0;5;5;4;4;5;5;4;4;2;5;5;0;0;3;3;0;0;5;6;0;0;0;0;0;2;2;1;2;1;2;2;2;4;3;4;1;5;4;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;4;0;0;0;0;0;0;0;
3;03.2012;0;2;2;2;4;3;4;4;4;3;3;3;2;4;6;1;1;1;6;5;1;6;6;1;1;1;1;1;2;2;1;2;2;0;2;2;3;4;2;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;3;3;4;4;0;0;0;0;0;0;0;
3;03.2012;0;1;3;1;4;4;3;3;4;4;4;4;2;5;5;1;1;4;6;5;1;4;1;1;1;1;1;5;2;1;1;2;0;0;1;2;4;4;3;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;3;0;0;0;0;0;0;0;

如果我正确理解了要求,则输出正确。

要删除1到5之间的字段:

zsh-4.3.14[t]% awk -F\; -v fl='1 2 3 4 5' -f s.awk OFS=\; infile > outfile
zsh-4.3.14[t]% cat outfile
3;1;3;4;3;1;3;4;3;2;0;4;4;1;1;4;2;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;3;0;3;1;3;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
2;0;5;5;4;4;5;5;4;4;2;5;5;0;0;3;3;0;0;5;6;0;0;0;0;0;2;2;1;2;1;2;2;2;4;3;4;1;5;4;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;4;0;0;0;0;0;0;0;
2;2;4;3;4;4;4;3;3;3;2;4;6;1;1;1;6;5;1;6;6;1;1;1;1;1;2;2;1;2;2;0;2;2;3;4;2;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;3;3;4;4;0;0;0;0;0;0;0;
3;1;4;4;3;3;4;4;4;4;2;5;5;1;1;4;6;5;1;4;1;1;1;1;1;5;2;1;1;2;0;0;1;2;4;4;3;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;3;0;0;0;0;0;0;0;

我错过了什么吗?

答案 4 :(得分:1)

Pure Bash:

IFS=';'
while read -a line ; do
  unset line[2]
  echo "${line[*]}"
done < infile.dat

答案 5 :(得分:1)

awk -F";" 'BEGIN{OFS=";"} {$3="";print }' file3|sed 's/;;/;/'

这是测试:

pearl.341> cat file3
3;03.2012;7228;0;1;3;1;3;4;3;1;3;4;3;2;0;4;4;1;1;4;2;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;3;0;3;1;3;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
3;03.2012;7229;0;2;2;0;5;5;4;4;5;5;4;4;2;5;5;0;0;3;3;0;0;5;6;0;0;0;0;0;2;2;1;2;1;2;2;2;4;3;4;1;5;4;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;4;0;0;0;0;0;0;0;
3;03.2012;7230;0;2;2;2;4;3;4;4;4;3;3;3;2;4;6;1;1;1;6;5;1;6;6;1;1;1;1;1;2;2;1;2;2;0;2;2;3;4;2;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;3;3;4;4;0;0;0;0;0;0;0;
3;03.2012;7231;0;1;3;1;4;4;3;3;4;4;4;4;2;5;5;1;1;4;6;5;1;4;1;1;1;1;1;5;2;1;1;2;0;0;1;2;4;4;3;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;3;0;0;0;0;0;0;0;

输出:

pearl.342> awk -F";" 'BEGIN{OFS=";"} {$3="";print }' file3 | sed 's/;;/;/'
3;03.2012;0;1;3;1;3;4;3;1;3;4;3;2;0;4;4;1;1;4;2;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;1;1;3;0;3;1;3;0;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;
3;03.2012;0;2;2;0;5;5;4;4;5;5;4;4;2;5;5;0;0;3;3;0;0;5;6;0;0;0;0;0;2;2;1;2;1;2;2;2;4;3;4;1;5;4;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;4;0;0;0;0;0;0;0;
3;03.2012;0;2;2;2;4;3;4;4;4;3;3;3;2;4;6;1;1;1;6;5;1;6;6;1;1;1;1;1;2;2;1;2;2;0;2;2;3;4;2;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;3;3;4;4;0;0;0;0;0;0;0;
3;03.2012;0;1;3;1;4;4;3;3;4;4;4;4;2;5;5;1;1;4;6;5;1;4;1;1;1;1;1;5;2;1;1;2;0;0;1;2;4;4;3;1;4;3;2;0;0;0;0;0;0;0;0;0;0;4;4;4;4;3;0;0;0;0;0;0;0;

答案 6 :(得分:0)

  
    

我需要删除字段3 ...是否有快捷方式说'$ 4到最后'

  

是的,它基本上问这个问题Print Field 'N' to End of Line

awk -F\; '{print $1 FS $2 FS substr($0, index($0, $4))}' temp.txt

这也处理奖金问题

FS是字段分隔符 所以我的7个字段的文件中的输出由';'分隔如下

  

awk -F \; '{print $ 1 FS $ 2 FS substr($ 0,index($ 0,$ 4))}'temp2

     

$&GT; FIELD1; FIELD2; field4中;字段5;字段6;字段7

注意: 打印字段N到最后会自然保留字段分隔符 - 至少据我所知

答案 7 :(得分:0)

在测试时我发现(如宣布的)我自己的sed解决方案:

sed -r 's/(([^;]*;){3}).;(.*)/\1\3/' a.txt > g.txt

如果你知道sed,不容易阅读,但写得容易。看起来好像我要为我的问题找到2个解决方案:用一个程序删除,用另一个程序转换。

删除字段3(如果我们碰巧从0开始计数,而不是1):)。