【问题标题】:tr command in awk to change the column valuesawk 中的 tr 命令更改列值
【发布时间】:2015-03-24 18:56:29
【问题描述】:

我在 awk 中使用我的 shell 脚本 TR 命令来屏蔽数据。当我在 awk 中使用 tr 命令时,下面的示例文件仅影响我的文件的第一行。当我在 while 循环中使用相同的并在其中调用 awk 命令时,它工作正常,但需要很长时间才能完成。现在我的要求是我想在同一个文件(file.txt)中屏蔽许多列[例如:$1、$5、$9],这应该影响整个文件而不是第一行,我想尽可能快地实现这一点来屏蔽数据。请指教

猫文件.txt
========
abcbchs,degehek
abcbchs,degehek
abcbchs,degehek
abcbchs,degehek
abcbchs,degehek
abcbchs,degehek,lskjsjshsh
abcbchs,degehek
abcbchs,degehek,lskjsjsshsh

输出

awk -F"," -v OFS=","  '{ "echo \""$1"\" | tr \"a-c\" \"e-f\" | tr \"0-5\" \"6-9\"" | getline $1 }7' file.txt

effffhs,degehek
abcbchs,degehek
abcbchs,degehek
abcbchs,degehek
abcbchs,degehek
abcbchs,degehek,lskjsjshsh
abcbchs,degehek
abcbchs,degehek,lskjsjsshsh

预期输出

effffhs,degehek
effffhs,degehek
effffhs,degehek
effffhs,degehek
effffhs,degehek
effffhs,degehek,lskjsjsshsh
effffhs,degehek
effffhs,degehek,lskjsjsshsh

【问题讨论】:

  • 您正试图在 awk 中运行 bash 代码,但 awk 是一种与 bash 完全不同的语言。如果您想在每一行上运行 bash 代码,请改用 while read loop
  • 它看起来有点像您从here 复制/粘贴代码,但这确实不是一种惯用或常用的方法。
  • @thatotherguy .. while 循环读取文件的整行,我尝试了它的工作正常和预期的结果,但时间因素是问题..我用于用不同的分隔符屏蔽不同类型文件中的数据,列也因文件而异,我希望使用 awk 是最好的方法......如果我们在 awk 命令行中执行以获得预期的结果。然后我会调用我的 ksh shell 脚本..

标签: linux unix awk tr


【解决方案1】:

您找到的代码在每个输入行上运行一个外部 shell 命令管道。就像你发现的那样,这是一种非常低效的方式来做你所要求的。 awk 根本不是这个任务的理想选择。也许试试 Perl。

perl -F, -lane '$F[$_] =~ tr/a-c/e-f/ =~ tr/0-5/6-9/ for (0, 4, 8); print join(",", @F)' file

-F, 选项类似于 Awk,但 Perl 不会自动拆分输入行。使用-a,它会拆分成一个名为@F 的数组,使用-n,它会遍历所有输入行。 -l 可以方便地从每个输入行中删除换行符并在打印时添加一个。

注意列是如何从零开始编号的,而不是像在 Awk 中那样的;所以for 循环中的索引访问@F 的第一个、第五个和第九个元素。

【讨论】:

  • 根据 OP 的切线评论,它尝试修改多个列。作为副作用,如果少于九个,它会添加新的空列。尝试使用具有更多列的输入数据,或更改索引以仅操作您实际拥有的列。
  • @user2449709 虽然您对 Ed Morton 的回答发表了评论,但让我们在这里继续讨论。如果您无法让此脚本正常工作,我需要更多信息来诊断问题所在。这个对我有用。随意在ideone.com/IvGUJW 上玩这个演示
【解决方案2】:

您在每次调用后忘记close() 命令。下面是正确的写法:

$ cat tst.awk
BEGIN { FS=OFS="," }
{
    cmd="echo '" $1 "' | tr 'a-c' 'e-f' | tr '0-5' '6-9'"
    $1 = ( (cmd | getline line) > 0 ? line : $1 )
    close(cmd)
    print
}

$ awk -f tst.awk file
effffhs,degehek
effffhs,degehek
effffhs,degehek
effffhs,degehek
effffhs,degehek
effffhs,degehek,lskjsjshsh
effffhs,degehek
effffhs,degehek,lskjsjshsh

您也没有保护自己免受 getline 故障的影响,因此 getline 调用的额外复杂性,请参阅http://awk.info/?tip/getline

鉴于您的 cmets,这显示了如何同时修改多个字段(在本例中为 1、3 和 5):

$ cat tst.awk
BEGIN { FS=OFS="," }
{
    cmd = "echo '" $0 "' | tr 'a-c' 'e-f' | tr '0-5' '6-9'"
    new = ( (cmd | getline line) > 0 ? line : $1 )
    close(cmd)
    split(new,tmp)
    for (i in tmp) {
        if (i ~ /^(1|3|5)$/) {
            $i = tmp[i]
        }
    }
    print
}

$ cat file
abc,abc,abc,abc,abc
abc,abc,abc,abc,abc,abc,abc
abc,abc,abc,abc,abc,abc
abc,abc,abc,abc

$ awk -f tst.awk file
eff,abc,eff,abc,eff
eff,abc,eff,abc,eff,abc,abc
eff,abc,eff,abc,eff,abc
eff,abc,eff,abc

处理输入数据中的引号:

$ cat tst.awk
BEGIN { FS=OFS="," }
{
    gsub(/'/,SUBSEP)
    cmd = "echo '" $0 "' | tr 'a-c' 'e-f' | tr '0-5' '6-9'"
    new = ( (cmd | getline line) > 0 ? line : $1 )
    close(cmd)
    split(new,tmp)
    for (i in tmp) {
        if (i ~ /^(1|3|5)$/) {
            $i = tmp[i]
        }
    }
    gsub(SUBSEP,"'")
    print
}

$ cat file
a'c,abc,a"c,abc,abc
abc,a'c,abc,a"c,abc,abc,abc
abc,abc,abc,abc,abc,abc
abc,abc,abc,abc

$ awk -f tst.awk file
e'f,abc,e"f,abc,eff
eff,a'c,eff,a"c,eff,abc,abc
eff,abc,eff,abc,eff,abc
eff,abc,eff,abc

如果您没有保证不会出现在输入中的任何特定控制字符,您可以使用https://stackoverflow.com/a/29237745/1745001末尾描述的技术创建一个不存在的字符串来代替上面的 SUBSEP

【讨论】:

  • 虽然建议不错,但这似乎并不能解决 OP 提出的效率问题。
  • OP 在他的问题中没有提到任何效率问题,只是说在 shell 循环中调用 awk 命令需要很长时间,我强烈怀疑这是因为他在他的 shell 中做了一些愚蠢的事情循环就像为他的文件的每一行在他的整个文件上调用一次 awk 一样,但他并没有向我们展示这么 idk。他可以试试这个,如果太慢,我们可以从那里拿走……
  • @Ed Morton.. 我试过你的脚本,它影响了第一列的整个文件.. 收到了预期的输出,但时间仍然是因素.. 我试图用 240 MB 文件操作1 列受到影响需要一个小时,即使在我的脚本中需要相同的持续时间并且我验证过,每列需要 1 小时的另一个问题,然后 8 列需要 8 小时。我希望有办法一次操作要屏蔽的多列,我想减少处理时间。
  • @Ed Morton。我的代码 ======= 用于 echo "5,6,8" | sed 's/,/ /g' 中的 COL 做 while read lne;做回声“$lne”| awk -F"$DEL" -v OFS="$DEL" '{ "echo \""$'$COL'"\" | tr \"a-c\" \"e-f\" | tr \"0-5\ " \"6-9\"" | getline $'COL' }7' >> file_bak ;完成
  • @Ed Morton ..感谢您的帮助和想法,它减少了文件包含要更改的多列的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 2017-07-26
  • 2015-04-11
  • 2014-07-07
  • 1970-01-01
  • 1970-01-01
  • 2016-12-11
相关资源
最近更新 更多