一个参数扩展可以在另一个参数扩展中工作吗?

一个参数扩展可以在另一个参数扩展中工作吗?
$ s=/the/path/foo.txt

我们可以通过不同的标准分别提取

$ echo ${s##*/}
foo.txt
$ echo ${s%.txt}
/the/path/foo

但如果我们想同时根据这两个标准进行提取,

$ echo ${${s##*/}%.txt}
bash: ${${s##*/}%.txt}: bad substitution

是否可以仅使用参数扩展而不引入临时变量来实现相同的目标?

参数扩展可以以某种方式在另一个参数扩展中工作吗?

谢谢。

答案1

不,又是。在 Bash 或标准 shell 中第一部分扩展必须是范围(即变量或位置参数,或特殊参数之一),而不仅仅是任何单词。

重击:

参数扩展的基本形式是${范围}。的价值范围被替换。这范围是如上所述的 shell 参数或数组引用。

POSIX 中的文本同样只提到参数。

您可以使用扩展其他部分扩展,因为它们可以是任意单词。但这当然无助于同一字符串的链接操作(就像在您的示例中一样${${s##*/}%.txt}

$ bash -c 's=/the/path/foo.txt; ext=.tyt; echo "${s%${ext/y/x}}"'
/the/path/foo

Zsh 明确支持不过,链接:

如果使用 ${...} 类型参数表达式或 $(...) 类型命令替换来代替姓名上面,它首先被扩展,并且结果被使用,就好像它是姓名

答案2

如果您正在寻找无扩展名文件名组件(即foo)作为单个操作,您可以使用 RE 匹配从路径中提取它:

s=/the/path/foo.txt
[[ "$s" =~ (.*/)?(.*)\. ]]

echo "Got > ${BASH_REMATCH[2]} <"    # "Got > foo <"

我还没有找到一种方法来从文件名中删除最短的未知扩展名,例如foo.bar.txt同时仍然处理根本没有扩展名的文件名组件(例如foo),除非您乐意接受复合表达式:

[[ "$s" =~ (.*/)?(.*)(\.[^.]*)$ ]] || [[ "$s" =~ (.*/)?(.*) ]]

如果你这样做,连续执行两次替换然后走开会更有效:

s=/path/to/foo
f=${s##*/}
echo "${f%.*}"

相关内容