Bash $ {..//../ ..}没有改变所有出现

时间:2016-05-30 13:38:56

标签: bash sh

我需要更换"微不足道的"名字的一部分。这需要在bash脚本中完成。要做到这一点,我需要删除中间词" VAN"," DEN"," DE"和" DER"。

为此,我使用内置替换(问题减少到2行):

line="STIG VAN DE WYNKELE"; 
line=${line//@(' VAN '|' DEN '|' DE '|' DER ')/' '};
echo $line;

输出:

STIG DE WYNKELE

预期产出:

STIG WYNKELE

似乎@(...)匹配其中一个中间词,删除了这一个中间词的所有出现,但它并不匹配其他词。

问题:我的语法错误了吗?如果没有,我该如何删除这些词? sed需要文件,而我的输入是变量,更改的文本也应存储在变量中。 ($ line应该改变)

4 个答案:

答案 0 :(得分:4)

您需要设置extglob选项。另外,删除引号,并将空格移到替代项之外。您可以进一步缩短表达式:

#!/bin/bash
line="STIG VAN DE DEN DER WYNKELE"
shopt -s extglob
line=${line//@(VAN|DE?([NR])) }
echo "$line"

通过在最后一行中双引号$,您可以看到空格是否被正确删除。

答案 1 :(得分:3)

bash没有回溯。首先,它在输入中找到VAN

STIG VAN DE WYNKELE
    ^^^^^|

(其中|表示扫描时的指针。)

VAN替换后,您有

STIG DE WYNKELE
     |

您会注意到从DE开始的字符串中找不到D; bash未检查您刚刚插入的空间。

相反,从每个模式中删除前导空格,并删除匹配而不是用空格替换它:

echo "${line//@('VAN '|'DEN '|'DE '|'DER ')}"

当然,问题在于你现在可能会删除在单词结尾处发生的匹配。只用一场比赛就可以避免这种情况;相反,在循环中进行多次替换:

for word in VAN DEN DE DER; do
    line=${line// $word / }
done

答案 2 :(得分:0)

您不需要任何extglob模式。您可以使用参数扩展:

${line/ * / }

示例:

$ line="STIG VAN DE WYNKELE"
$ echo ${line/ * / }
STIG WYNKELE

答案 3 :(得分:0)

使用awk:

echo $line | awk '{ if ($2 == "VAN" || $2 == "DEN" || $2 =="DE" || $2=="DER"  ) $2="";  if ($3 == "VAN" || $3== "DEN" || $3 =="DE" || $3=="DER"  ) $3="" ; print }'