【问题标题】:Using awk to print a new column without apostrophes or spaces使用 awk 打印没有撇号或空格的新列
【发布时间】:2017-08-29 14:43:35
【问题描述】:

我正在处理一个文本文件并添加一个由其他列的某些组件组成的列。提出了删除空格和撇号的新要求,但我不确定完成此任务的最有效方法。

文件的内容可以通过以下脚本创建:

content=(
  john    smith          thomas       blank    123    123456    10  
  jane    smith          elizabeth    blank    456    456123    12  
  erin    "o'brien"      margaret     blank    789    789123    9  
  juan    "de la cruz"   carlos       blank    1011   378943    4
)
# put this into a tab-separated file, with the syntactic (double) quotes above removed
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "${content[@]}" >infile

这是我现在拥有的,但它无法删除空格和撇号:

awk -F "\t" '{OFS="\t"; print $1,$2,$3,$5,$6,$7,$6 tolower(substr($2,0,3)); }' infile > outfile

这会引发错误“子第三个参数不是可更改的对象”,这是有道理的,因为我想我正在尝试处理输出而不是输入。

awk -F "\t" '{OFS="\t"; print $1,$2,$3,$5,$6,$7,$6 sub("'\''", "",tolower(substr($2,0,3))); }' infile > outfile

有没有办法可以将第 6 列和第 2 列的一部分以小写形式打印,同时从输出中删除空格和撇号到新列?在最坏的情况下,我可以使用我的第一个命令创建一个新文件并使用新的 awk 命令处理该输出,但我想一次性完成。

【问题讨论】:

  • 您是否有理由为此需要awk,而不是使用(比如说)bash 内置功能?
  • 提供一个不需要别人提供自己的infile 进行测试的独立示例也会很有帮助。
  • 我没有 awk 作为要求,但考虑到我正在处理一个包含制表符分隔内容的文件并且我不希望将更改应用于所有列,这似乎是一个合乎逻辑的解决方案。如果您有其他不使用 awk 的解决方案,我绝对愿意接受其他选择。
  • edit 提供输入和所需的输出——否则,有可能解决方案的人无法测试它们。这可能只是一个生成输入的命令,例如:{ printf '%s\t' foo bar "two words" "'five words enclosed in quotes'" baz; printf '\n'; } | awk ...;另请参阅有关构建 minimal reproducible example 的文档。
  • 也就是说,在 bash 中,while IFS=$'\t' read -r -a words; do ... 将遍历输入流,将每个部分放入数组 words 的一个元素中; ${foo//[[:space:]\']} 将评估为 foo 的一个版本,其中删除了空格和撇号......这是你需要在没有 awk 的情况下执行此操作的重要部分。

标签: bash awk


【解决方案1】:

第二种方法很接近,但是对于操作顺序:

awk -F "\t" '
  BEGIN { OFS="\t"; }
  {
    var=$2;
    sub("['\''[:space:]]", "", var);
    var=substr(var, 0, 3);
    print $1,$2,$3,$5,$6,$7,$6 var;
  }
'
  • 将您要修改的内容分配给变量可以就地修改该变量。
  • 您要删除的字符应该在获取子字符串之前删除,否则会缩短 3 个字符的子字符串。

【讨论】:

  • 你忘了照顾双引号,所以它打印 789123"ob,应该是 789123obr。
  • @mlambichs, ...鉴于您提供的输入,此代码绝对会打印 78123obr,因为该输入中的唯一引号是单引号,而不是双引号。您只指定了“撇号”——那些是单引号。所以,不,我什么都没忘记。
  • 你找错树了。 ;-) 我刚刚检查了您的解决方案,仅此而已。我用早期版本的 OP 的输入检查了它。我的错。
  • @mlambichs,...哦,对了——那不是你;对不起'回合。 (查看问题的历史,我没有看到任何具有o"brien 的问题版本——也许 OP 在之前的问题上使用了类似的数据?)
  • 没有。刚检查。有一个让我绊倒的“o'brien”。
【解决方案2】:

这是一个猜测,因为您没有提供预期的输出,但这是您想要做的吗?

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{
    abbr = $2
    gsub(/[\047[:space:]]/,"",abbr)
    abbr = tolower(substr(abbr,1,3))
    print $1,$2,$3,$5,$6,$7,$6 abbr
}

$ awk -f tst.awk infile
john    smith   thomas  123     123456  10      123456smi
jane    smith   elizabeth       456     456123  12      456123smi
erin    o'brien margaret        789     789123  9       789123obr
juan    de la cruz      carlos  1011    378943  4       378943del

请注意,在 ' 封闭的 awk 脚本中表示 ' 的方法是使用八进制 \047(如果/当您将脚本移动到文件时,它将继续工作,不像您依赖在 "'\''" 上,它只能从命令行工作),并且 awk 中的字符串、数组和字段从 1 开始,而不是 0,所以你的 substr(..,0,3) 是错误的,awk 将 0 的无效开始位置视为如果您使用了第一个有效的起始位置,即1

你得到的"sub third parameter is not a changeable object" 错误是因为sub() 修改了你调用它的对象作为第三个参数,但是你用文字字符串(tolower(substr(...)) 的输出)调用它,你不能修改文字字符串 - 尝试 sub(/o/,"","foo") 会得到与使用 var="foo"; sub(/o/,"",var) 相同的错误,这是有效的,因为您可以修改变量的内容。

【讨论】:

  • 我们两个答案的输出之间的差异——我的发出 789123obr,而不是 789123ob
  • 对,因为您在 substr() 调用之前删除了 ',然后我正在这样做。我只是在关注 OP 在他的代码中的内容(在 substr() 调用之后删除 ') - 因为他没有发布正确的预期输出 idk。
  • 对。如果 OP wanted 在输出中少于三个字符的子字符串,我会感到非常惊讶。
  • 我认为你可能是对的,并且 OP 确实想先删除空格和撇号,所以我更新了我的答案。
猜你喜欢
  • 2017-11-22
  • 2011-04-26
  • 2014-03-06
  • 2012-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-31
  • 1970-01-01
相关资源
最近更新 更多