从字符串中删除一个单词

Deleting a word from a string

我想从文件中删除特定单词。

假设文件名 agent_file.txt 包含以下字符串

-queues winall,dustat,envstat,netstat,iostat,winconfig,netwarestat,netwareconfig,pawmin,paw15,db2,sqlserver,vmstatvmw2,vmstatvm2,netstatvm,netstatvmw,vmstatvm,vmstatvmw,iostatvm,iostatvmw,envstatvm,envstatvmw,vmscpu,vmsdisk,vmsmem,vmstatvcw,process,winprocess

我只想删除这个文件中的字符或单词process,所以我在Unix下使用下面的命令

perl -pi -e 's/process//g' agent_file.txt

输出将是

-queues winall,dustat,envstat,netstat,iostat,winconfig,netwarestat,netwareconfig,pawmin,paw15,db2,sqlserver,vmstatvmw2,vmstatvm2,netstatvm,netstatvmw,vmstatvm,vmstatvmw,iostatvm,iostatvmw,envstatvm,envstatvmw,vmscpu,vmsdisk,vmsmem,vmstatvcw,,win

该词被删除,但也删除了winprocess的一部分。

如何只删除 process 以及前面的逗号 ,(如果有)?

输出应该是

-queues winall,dustat,envstat,netstat,iostat,winconfig,netwarestat,netwareconfig,pawmin,paw15,db2,sqlserver,vmstatvmw2,vmstatvm2,netstatvm,netstatvmw,vmstatvm,vmstatvmw,iostatvm,iostatvmw,envstatvm,envstatvmw,vmscpu,vmsdisk,vmsmem,vmstatvcw,winprocess

分三步完成

sed 's/,process,/,/g;s/^process,//;s/,process$//'   

测试位置:中间、开始或结束。

如果它是逗号分隔的,请使用它们来使它成为一个词。

perl -pi -e "s/, \s* process\s* , /, /g filename 

如果逗号前后没有空格,则删除 \s*

使用逗号 (-F,) 自动拆分 (-a),完全避免逗号问题

perl -F, -lane 'print join ",", grep { not /^process$/ } @F' input  > output

输入被 , 分解为 @Fgrep 过滤掉单词,其余的由 ,

加入

为了更改输入文件,添加 -i 并删除 > output

Command switches in perlrun


问题是第一个和最后一个单词有一个逗号,如果去掉就需要去掉,而其他的有两个逗号,需要留下一个。正则表达式的一种方法是进行两次传递,删除单词,然后删除额外的逗号(仍然要小心 first 和 last)。

或运行替换部分中的代码适用于select情况

echo "go,stay,ago,go,got,end,go" | 

    perl -pe's/(,)?\bgo\b(,)?/ &&  && ","/ge'

打印:stay,ago,got,end/e 将替换端评估为 Perl 代码。

如果两个逗号都存在 ( && ) 则 (&&) 我们用逗号 (",") 替换。

这是可行的,因为在 Perl 中 && returns the value

The ||, // and && operators return the last value evaluated (unlike C's || and &&, which return 0 or 1).

这是一个 awk 解决方案。

awk 'BEGIN{OFS=FS=","} {for(i=1;i<=NF;i++) if($i=="process") $i=""} 1' file

这会将您的字段分隔符设置为输入和输出的逗号,然后遍历您的字段,检查是否与您感兴趣的字符串等价,如果匹配则清空该字段。最后的 1 是 "print the current line".

的 awk 简写

同样,可以按记录而不是按字段对输入数据进行切片:

awk 'BEGIN{ORS=RS=","} /^process$/{next} 1' file

awk 'BEGIN{ORS=RS=","} [=12=]=="process"{next} 1' file

这会使用逗号作为 RECORD 分隔符 (RS) 遍历您的输入,以便可以使用 awk 的记录感知来评估单个单词。您可以通过正则表达式或不通过正则表达式进行评估——正则表达式稍微慢一点,但似乎可以为您节省两个字符的输入。 :-)

使用 RS/ORS 的策略消除了您注意到的字段被清空但未被删除的问题。当逗号是记录分隔符时,跳过的记录会导致不显示记录分隔符,这更接近您在问题中包含的 "ideal" 输出。


最后一个选项可能是在您的 shell (ksh) 中执行此操作。这将具有最大的可移植性优势(您不必担心 perl 版本,无论 awk/sed 是 GNU 还是 BSD 或其他)。缺点是使用下面的方法,您的文件大小将受到系统内存的限制(可能还有可配置的限制)。

$ IFS=, read -A arr < file
$ for i in "${!arr[@]}"; do [[ "${arr[$i]}" == "process" ]] && unset arr[$i]; done
$ output=$(printf "%s," "${arr[@]}")
$ echo "${output%,}"
-queues winall,dustat,envstat,netstat,iostat,winconfig,netwarestat,netwareconfig,pawmin,paw15,db2,sqlserver,vmstatvmw2,vmstatvm2,netstatvm,netstatvmw,vmstatvm,vmstatvmw,iostatvm,iostatvmw,envstatvm,envstatvmw,vmscpu,vmsdisk,vmsmem,vmstatvcw,winprocess

请注意,仅需要 $output 变量才能去除 printf 生成的尾随逗号。另一种选择可能是更全局地设置 $IFS

$ IFS=,
$ echo "${arr[*]}"
-queues winall,dustat,envstat,netstat,iostat,winconfig,netwarestat,netwareconfig,pawmin,paw15,db2,sqlserver,vmstatvmw2,vmstatvm2,netstatvm,netstatvmw,vmstatvm,vmstatvmw,iostatvm,iostatvmw,envstatvm,envstatvmw,vmscpu,vmsdisk,vmsmem,vmstatvcw,winprocess

我应该指出,尽管出现,printf 选项并没有真正产生外部命令,因为 ksh 将 printf 作为内置实现。

也许像这样?

它搜索所有出现的 process,使用单词边界来确保它不会作为另一个单词的一部分找到,并且还在前后匹配可选的逗号 ,。如果找到两个逗号(process 位于列表中间),则匹配项将替换为单个逗号,如果只有一个(process 仅出现在列表的开头或结尾,则将其删除列表)

perl -pi -e 's/ ,? \b process \b ,? ) /  =~ tr/,// > 1 ? ',' : '' /xge' agent_file.txt
awk '{sub(/process,/,"")}1' file