【问题标题】:Delete a field by position from a long line从长行中按位置删除字段
【发布时间】:2012-04-11 03:51:36
【问题描述】:

我有一个很长的分号分隔的字段行,准确地说是 69 个字段。

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

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

这会变得很长。有没有捷径可以说“4 美元到最后”、“4 美元到 69 美元”或者只是“删除 3 美元”?

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

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

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

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

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

我想我可以快速找到 sed 解决方案,但我还有更多事情要做(重新计算字段 5:如果字段 1 == 2,Field5 = 5-Field5),这在 sed 中很难,但我猜非常适合 awk。

如果重要的话,我正在使用 Gnu-AWK 3.1.6,但是根据 apropos:

  • awk
  • 呆呆
  • igawk
  • 莫克
  • 不知道
  • 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;

只要守住线。 :)

【问题讨论】:

标签: linux bash csv awk


【解决方案1】:

我很抱歉打断这场不正当的高尔夫比赛。你们受虐狂是否乐于重新发明轮子?文明为现代人提供了污水收集和 CSV 图书馆等便利设施,因此他不必处理——

csvfix 怎么样?它是一个命令行工具,适用于输入和输出的文本,即。和awk一样的环境。你需要的命令是exclude

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

【讨论】:

  • +1。是的,真的。这些笨拙的解决方案仅适用于无法安装任何第三方代码的 Unix (TM) 系统安装。 (包括 GNU Awk:谈论一个对 awk 语言的扩展完全没有意义的程序,因为如果你可以安装 GNU 任何东西,你就可以穿上你的橡胶靴,穿过你认为妨碍你的 15 英尺的 gawk去别的地方。)
  • 对不起,不在我的系统上,也不在存储库中。当然,我可以安装它,但是我也可以使用cut,或者sed。但它看起来又漂亮又简单。没关系。
【解决方案2】:

一种方式:

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;

【讨论】:

  • +1 通用解决方案。但是,最后一个“;”不应该在那里。 ;)
  • 输出合并到一个大行中,所以我在两个}} 之间放了一个printf "\n"。现在我看到所有 0 的值都被丢弃了。那是-我很抱歉-不可接受。否则,它看起来安静优雅。 (我同时在上面的问题中添加了测试数据)。代替delete f[3],我们可以删除该行,稍后再进行测试:printf "%s", i!=4 ? f[i] ";" : ""?
【解决方案3】:

您可以改用cut 命令:

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

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

如果您仍然需要在 awk 中处理结果,您可以通过管道将其输出到其中。

【讨论】:

  • 完美运行。可用。短而优雅。不是 awk,但值得一票。 :)
【解决方案4】:
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;

【讨论】:

  • 你迟到了,但是代码可以工作,非常优雅,易于理解,没有使用令人困惑的临时 ,不需要的多余,不同的语言 - 只是有点 sed结束,但所有其他帖子都有更多次要或不那么次要的问题。 :)
  • 很遗憾,如果您清空 $3 但默认情况下 $2 为空,这将失败。
【解决方案5】:

你可以这样使用:

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

要删除第三个字段:

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

[编辑:在此处关注 cmets]

给定样本输入:

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;

我错过了什么吗?

【讨论】:

  • 嗯。我将第一个代码从撇号保存到一个文件 del3b.awk 独有的撇号。我用awk -v fl=3 -f del3b.awk a.txt &gt; h.txt 调用它,但xxdiff a.txt h.txt 显示没有区别。既然这部分已经解决了,我对纠正这个不如预期的那么优雅的代码不太感兴趣,但是由于我投票支持 ltn100 的剪切解决方案,我无法抗拒......
  • 您是否设置了字段分隔符 (FS)? awk -F\; -v fl=...
  • 不,但现在我做到了,与原始文件的唯一区别是尾随 ;,它被删除了。
  • 嗨@user unknown,我在你的示例文件中添加了一个示例。
  • 现在它可以工作了,我无法重建出了什么问题。 :) 也许我偶然发现了你的一个重命名。 &lt;filed_list&gt; 一开始在我看来就像一个文件重定向,我从来没有谈论过一个列表和一次删除多个的必要性。
【解决方案6】:

纯猛击:

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

【讨论】:

  • 我需要稍后重置 IFS,不是吗?
【解决方案7】:

我需要删除字段 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

$> field1;field2;field4;field5;field6;field7

注意: 将字段 N 打印到末尾自然会保留字段分隔符——至少据我了解

【讨论】:

  • 我很生气。对于我的问题中的真实数据和测试数据,您的第一个代码会产生3;03.2012;03.2012;7228;0;1;3;1;3;4...,这意味着在输出中重复字段 2 - 而不是删除字段 3。但是对于使用echo "1;2;3;4;..." 的简单测试,它可以工作。我不知道那里发生了什么。它看起来如此精致和容易!同时我观察到需要删除的是字段 4(我通常从 0 开始计数),但这并没有什么区别。
  • 这是一个奇怪的问题。我会调查的。
【解决方案8】:

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

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

如果您了解 sed,则不容易阅读,但容易编写。看起来我要使用两种解决方案来解决我的问题:使用一个程序删除,并使用另一个程序进行转换。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-13
    • 2015-11-01
    • 1970-01-01
    • 2017-06-25
    • 2015-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多