在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
或任何其他外部字符串操作程序的情况下获得正确的结果?
答案 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