【问题标题】:sed: remove whole words containg a character classsed:删除包含字符类的整个单词
【发布时间】:2014-09-29 06:57:09
【问题描述】:

我想从文本文件中删除任何包含非字母字符的单词。例如

"ok 0bad ba1d bad3 4bad4 5bad5bad5"

应该变成

"ok"

我尝试过使用

echo "ok 0bad ba1d bad3 4bad4 5bad5bad5" | sed 's/\b[a-zA-Z]*[^a-zA-Z]\+[a-zA-Z]*\b/ /g'

【问题讨论】:

  • 您要删除的是非字母还是数字?你的尝试有什么问题?
  • 所有非字母,而不仅仅是数字。它产生了一个错误的答案。

标签: linux bash command-line awk sed


【解决方案1】:

使用awk:

s="ok 0bad ba1d bad3 4bad4 5bad5bad5"
awk '{ofs=""; for (i=1; i<=NF; i++) if ($i ~ /^[[:alpha:]]+$/)
         {printf "%s%s", ofs, $i; ofs=OFS} print ""}' <<< "$s"
ok

awk 命令循环遍历所有单词,如果单词与正则表达式/^[[:alpha:]]+$/ 匹配,则它会写入标准输出。 (i&lt;NF)?OFS:RS 是添加 OFS 的快捷方式,如果当前字段 no 小于 NF 否则它写入 RS

同时使用grep + tr

s="ok 0bad ba1d bad3 4bad4 5bad5bad5"
r=$(grep -o '[^ ]\+' <<< "$s"|grep '^[[:alpha:]]\+$'|tr '\n' ' ')
echo "$r"
ok

首先grep -o 将字符串分解为单个单词。第二个 grep 仅搜索带有字母的单词。最后tr\n 转换为空格。

【讨论】:

  • @Dimid:我在回答中添加了解释。
  • 我重复我在下面写的评论:请使用[[:alpha:]] 而不是错误的[a-zA-Z],因为如果您在s 中将您的“ok”替换为“Öcalan”,例如,您的脚本输出一个空字符串。 :(
  • @Jotne - 在某些 awks(例如 OSX awk)中,print &lt;ternary&gt; 将根据三元表达式中的条件导致语法错误,而 print (&lt;ternary&gt;) 将始终成功。我不确定是否还有其他情况会导致未加括号的三元表达式失败,我也不知道仅将表达式的条件部分加括号是否足以解决此问题。无论如何,我个人觉得给整个表达式加上括号更容易阅读,而且我知道它适用于所有情况,所以我就这样做了。
  • 我实际上在 OSX 上工作,并使用默认 OSX 版本的 awk 在 awk 上进行了测试。但是是的,为了更好的兼容性,使用(i&lt;NF?OFS:RS) 似乎会更好
  • 是的,正如我提到的,您的解决方案是添加尾随空白字符而不提供换行符。您需要awk '{ofs=""; for (i=1; i&lt;=NF; i++) if ($i ~ /^[[:alpha:]]+$/) {printf "%s%s", ofs, $i; ofs=OFS} print "" }' 或类似名称。
【解决方案2】:
 st="ok 0bad ba1d bad3 4bad4 5bad5bad5"
 for word in $st; 
     do 
     if [[ $word =~  ^[a-zA-Z]+$ ]]; 
         then 
             echo $word; 
      fi; 
 done

【讨论】:

  • 这不是在 bash 中分配变量的方式。检查this answer
  • 我错误地添加了 $ 和空格
【解决方案3】:

如果您不担心在每个单词之间丢失不同数量的空格,您可以在 Perl 中使用类似的东西:

perl -ane 'print join(" ", grep { !/[^[:alpha:]]/ } @F), "\n"

-a 开关启用自动拆分模式,该模式将文本拆分为任意数量的空格并将字段存储在数组 @F 中。 grep 过滤掉该数组中包含任何非字母字符的元素。生成的数组连接在一个空格上。

【讨论】:

    【解决方案4】:

    下面的 sed 命令可以完成这项工作:

    sed 's/[[:space:]]*[[:alpha:]]*[^[:space:][:alpha:]][^[:space:]]*//g'
    

    它会删除所有包含至少一个非字母字符的单词。最好使用像 [:alpha:] 这样的 POSIX 字符类,因为例如它们不会认为法语名称“François”有问题(即包含非字母字符)。

    说明

    我们删除所有以任意数量的空格开头的模式,后跟任意数量(可能为零)的字母字符,后跟至少一个非空格和非字母字符,然后 glob 到单词的末尾(即直到下一个空格)。请注意,您可能希望将 [:space:] 替换为 [:blank:],有关这两个 POSIX 类之间差异的详细说明,请参阅 this page

    测试

    $ echo "ok 0bad ba1d bad3 4bad4 5bad5bad5" | sed 's/[[:space:]]*[[:alpha:]]*[^[:space:][:alpha:]][^[:space:]]*//g'
    ok
    

    【讨论】:

    • +1,我自己试图按照这些思路解决问题。你在 perl 中的命令:perl -pe 's/\s*[[:alpha:]]*[^\s[:alpha:]]\S*//g'
    【解决方案5】:

    这可能对你有用(GNU sed):

    sed -r 's/\b([[:alpha:]]+\b ?)|\S+\b ?/\1/g;s/ $//' file
    

    这使用交替中的反向引用来保存所需的字符串。

    【讨论】:

      猜你喜欢
      • 2014-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-02
      • 2022-07-06
      • 2015-07-10
      • 1970-01-01
      • 2017-04-28
      相关资源
      最近更新 更多