用bash替换最短的字符串匹配

时间:2014-12-22 10:47:06

标签: string bash

在bash中,有几种有用的字符串操作模式,例如从字符串的开头/结尾删除最短/最长子字符串:

${var#substring}
${var##substring}
${var%substring}
${var%%substring}

然后还有替换模式,它替换字符串的任何部分的子字符串:

${var/substring/replacement}

这个问题是它很贪婪,总是取代最长的匹配。例如,如果我有一个像/a/b/foo-bar/x/y/z这样的目录名,并且我想将以 foo - 开头的任何子目录名替换为 baz ,那么它将无法工作我预计。我希望结果为/a/b/baz/x/y/z。我尝试了以下命令:

${PWD/\/foo-*\///baz/}

这种情况下的结果是/a/b/baz/z,因为模式匹配以/foo-开头并以/开头的最长子字符串。有没有办法在不调用sed或任何其他外部字符串操作程序的情况下获得正确的结果?

4 个答案:

答案 0 :(得分:2)

当然,你总是可以使用扩展的球:

shopt -s extglob

var=/a/b/foo-bar/x/y/z/foo-bar2/1/2/3
echo "${var//\/foo-*([^\/])\///baz/}"

很乐意输出

/a/b/baz/x/y/z/baz/1/2/3

答案 1 :(得分:1)

在纯BASH中你可以这样做(使用BASH正则表达式功能):

s='/a/b/foo-bar/x/y/z'
p="$s"
[[ "$s" =~ ^(.*/)'foo-'[^/]*(.*)$ ]] && p="${BASH_REMATCH[1]}baz${BASH_REMATCH[2]}"

echo "$p"
/a/b/baz/x/y/z

答案 2 :(得分:1)

这是一种在纯Bash中执行此操作的方法,它替换了与模式匹配的路径中的所有子目录。它将路径拆分为一个数组,将每个路径组件上的替换替换为一个新数组,然后使用printf替换路径分隔符。

name='/a/b/foo-bar/x/foo-y/z';
IFS=$'/';
aname=($name);
bname=();
for i in "${aname[@]}";
do bname+=("${i/foo-*/baz}");done;
printf -v newname "%s/" "${bname[@]}";

printf "%s\n" "$newname"

<强>输出

/a/b/baz/x/baz/z/

(抱歉所有的;,我刚从shell中做了快速复制和粘贴)

答案 3 :(得分:0)

你不能在bash的内置字符串替换中使用正则表达式。如果您必须坚持使用内置字符串操作,则必须首先提取子字符串foo-bar,然后在${PWD/$match/baz}中使用它。

但是,如果你可以使用正则表达式,你可以很容易地做到这一点。在linux / unix下有很多方便的字符串处理程序,可以很容易地做到这一点。例如:

kent$  sed 's#/foo-[^/]*#/baz#' <<< '/a/b/foo-bar/x/y/z'
/a/b/baz/x/y/z