变量中的纯壳复杂替换

变量中的纯壳复杂替换

有没有什么方法可以同时替换多个模式的变量中的文本,甚至使用反向引用?

例如,我有FILE=filename.ext并且我想将其更改为filename_sometext.ext.但我不知道文件扩展名是.ext.我所知道的是扩展名在最后一个点之后。所以我可以分两步完成:

EXT=${FILE##*.}
FILE=${FILE%.*}_sometext.$EXT

我可以一步完成(例如${FILE/.\(*\)/_sometext.\1}[这不起作用])吗?

顺便说一句,我需要在没有sed/ awk/etc 的纯 shell 中执行此操作。我的外壳是ksh,但如果有办法用 bashisms 做到这一点,我也想知道。

答案1

Bash 参数扩展表示变量(FILE在您的示例中)必须是参数名称。所以它们不筑巢。最后一部分${param/pattern/replacement}必须是字符串。因此不支持反向引用。

我唯一的建议是使用

${EXT:+.$EXT}

以避免在文件没有扩展名时添加尾随点。


更新

显然支持反向引用克什93

所以你可以使用类似的东西

FILE="${FILE/@(.*)/_something\1}"

答案2

在这种特殊情况下,我认为FILE=${FILE%.*}_sometext.${FILE##*.}可以完成这项工作。

答案3

变量。

在任何 shell 中,您都可以连接变量或值:

$ a=One; b=Two; c=Three
$ d="$a$b$c"; echo $d
OneTwoThree

${parameter%word}结构是 POSIX长期以来,大多数 shell 都支持该功能。
假设FILE=filename.ext,你可以这样做:

$ FILE="${FILE%.*}_sometext.${FILE##*.}"; echo "$FILE"
filename_sometext.ext

这是一行(根据要求)并且适用于大多数 shell。


也可以使用变量(即使有几个点):

#!/bin/bash
FILE=file.one.name.ext
ADDTEXT="_sometext"
EXT="${FILE##*.}"
echo "EXT=$EXT"
echo "final FILE=${FILE%.*}${ADDTEXT}.$EXT"

或者,只用一行:

ONEFILE=${FILE%.*}${ADDTEXT}.${FILE##*.}
echo "one   FILE=$ONEFILE"

模式替换

棘手的是尝试使用${parameter/pattern/string}“模式替换”。

变量可以在许多 shell 中工作(甚至 busybox ash):

NEWTEXT="${ADDTEXT}.${FILE##*.}"
echo "ash   FILE=${FILE/.*/${NEWTEXT}}"

但在较老的sh埃尔斯则不然。


仅适用于FILE=filename.ext
这适用于 bash(以及 ksh、ksh93、mksh、zsh),但不适用于 ash、dash、sh 或 csh

echo "bash  FILE=${FILE/%.*/${ADDTEXT}.${FILE##*.}}"

请注意,它使用 字符%来指示参数末尾的匹配(但是,遗憾的是它是贪婪的并且吃掉了 中的所有点FILE=file.one.name.ext)。

结论

控制替换贪婪程度的最佳方法是使用单独的变量(而不是“模式替换”中的一些变量。)

答案4

FILE="${FILE%.*}_sometext${FILE#"${FILE%.*}"}"

如果 shell 变量$FILE包含 a ,.上面的命令将重新分配给它旧值减去最后一个之后的所有.内容_sometext并保留旧值的最后一个.以及之后的所有内容。

所以:

FILE=some.dot
FILE="${FILE%.*}_sometext${FILE#"${FILE%.*}"}"
printf %s\\n "$FILE"

some_sometext.dot

如果 shell 变量$FILE不是包含一个点,那么_sometext字符串仅附加到旧值的末尾:

FILE=no_dots_at_all
FILE="${FILE%.*}_sometext${FILE#"${FILE%.*}"}"
printf %s\\n "$FILE"

no_dots_at_all_sometext

如果您没有像我最后那样嵌套参数扩展,那么这与您可能得到的行为有重要不同:

FILE=no_dots_at_all
FILE="${FILE%.*}_sometext${FILE##*.}"
printf %s\\n "$FILE"

no_dots_at_all_sometextno_dots_at_all

当您嵌套参数扩展时,它会从内到外进行求值,因此首先发生的事情是:

for    FILE in    some.dot no_dots_at_all
do     printf %s\\n '${FILE#'"${FILE%.*}"'}'
done

${FILE#some}
${FILE#no_dots_at_all}

...外壳删除所有匹配的位并使用剩余部分作为从外部扩展中剥离的模式。所以当有不是任何匹配的位和变量都会完全扩展,外部扩展会被剥离全部变量的值,而不是将其替换到表达式中两次。

相关内容