String 替换bash中的最短字符串匹配

String 替换bash中的最短字符串匹配,string,bash,String,Bash,在bash中,有几种有用的字符串操作模式,例如从字符串的开头/结尾删除最短/最长的子字符串: ${var#substring} ${var##substring} ${var%substring} ${var%%substring} ${var/substring/replacement} 然后还有替换模式,它替换字符串任何部分的子字符串: ${var#substring} ${var##substring} ${var%substring} ${var%%substring} ${var

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

${var#substring}
${var##substring}
${var%substring}
${var%%substring}
${var/substring/replacement}
然后还有替换模式,它替换字符串任何部分的子字符串:

${var#substring}
${var##substring}
${var%substring}
${var%%substring}
${var/substring/replacement}
问题是它很贪婪,总是替换最长的匹配。例如,如果我有一个像
/a/b/foobar/x/y/z
这样的目录名,并且我想替换任何以foo-to-baz开头的子目录名,那么它就不会像我期望的那样工作。我希望结果是
/a/b/baz/x/y/z
。我尝试了以下命令:

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

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

在bash的内置字符串替换中不能使用regex。如果必须坚持使用内置字符串操作,则必须先提取子字符串
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

kent$sed的#/foo-[^/]*#/baz#'在纯BASH中,您可以这样做(使用BASH正则表达式功能):


在纯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中快速复制并粘贴了)

当然,您可以始终使用扩展全局:

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

注意,如果变量不包含字符串
foo
,则此操作将失败。要解决这个问题,您必须添加一些测试,最终它会变得比extglobs更尴尬
:)
。好的,我更新了答案,以确保只有当原始字符串中有
foo-
时才进行赋值。在正则表达式之前,您可能需要
p=$s
,这样当
s
不匹配时,
p
自动扩展到
s
,这只执行一个替换:如果
s='/a/b/foo-bar/x/y/z/foo-bar 2/1/2/3'
,则只替换第一个
foo-bar
。@gniourf\u gniourf:是的,要进行多个替换,这里需要一个循环,而
extglob
是更好的选择。要在斜杠上拆分,您可以使用以下命令:
IFS=/read-r-d'-a aname<谢谢,@gniourf\u gniourf。我不知道你能做
“${aname[*]/foo-*/baz}”
;太好了!我必须承认,我甚至没有想过使用
read
。。。
-a
选项一定是我忘记了