命令替换可以嵌套在变量替换中吗?

命令替换可以嵌套在变量替换中吗?

我想对通过命令访问的特定字符串使用变量替换。例如,如果我将某些内容复制到剪贴板中,我可以像这样访问它。

$ xclip -o -selection clipboard
Here's a string I just copied.

如果我将它分配给一个变量,那么我可以对其进行变量替换。

$ var=$(xclip -o -selection clipboard)
$ echo $var
Here's a string I just copied.
$ echo ${var/copi/knott}
Here's a string I just knotted.

但是,有没有一种方法可以在不将其分配给变量的情况下进行变量替换?从概念上讲,是这样的。

$ echo ${$(xclip -o -selection clipboard)/copi/knott}
bash: ${$(xclip -o -selection clipboard)/copi/knott}: bad substitution

此语法失败,因为var应该是变量名,而不是字符串。

答案1

不,你不能。bash大多数其他 shell(除了zsh)不允许嵌套替换。

有了zsh,你可以做嵌套替换:

$ echo ${$(echo 123)/123/456}   
456

答案2

是的,你可以这样做——有点。确实不漂亮。它更像是排队比嵌套。问题是您必须对扩展的参数的值进行操作 - 如果该参数没有值,那么您将不会做太多事情。因此,您可以分配该值尽管扩大它,这几乎不是一条捷径。

v=; echo "${v:=${0##*["$0${v:=$(xsel -bo)}"]}${v/copi/knott}}"

我使用$0链内的参数扩展来隐藏分配。它在嵌套赋值扩展中分配 var 的值。外部优先 - 但因为它只会扩展到内部所做的任何事情,所以很难说。然而,如果我们沉默内部扩展,然后修改它,你就可以得到你想要的。将字符串复制到我的剪贴板后(我没有xclip——只是xsel它打印:

Here's a string I just knotted.

不过,如果你省略的话,会发生什么会更清楚$0

v=; echo "${v:=${v:=$(xsel -bo)}${v/copi/knott}}"

打印:

Here's a string I just copied.  Here's a string I just knotted.

...因为内部赋值发生在修改之前,但是,如上所述,外部赋值优先 - 并且它扩展到内部赋值的扩展到修改后的内部扩展。

当然,如果目标参数是已经分配 - 所以你只能在首先清空变量的情况下才能做到这一点......老实说,这可能是分配它的最方便的时间。

答案3

这是一个示例黑客,它绕过为链式参数替换分配临时变量:

declare -a values=( "4: " "#1" "#2" "#3" "#4" )
declare -A arr["VALUES"]="${values[@]}"

echo -e "Original: ${arr[@]}"

"${arr[@]#*: ?}" >&/dev/null; san="${_//#/}"

echo -e "Sanitized: $san"

解释:

我们使用arrarray 中的值初始化一个关联数组values。第一个元素包含值元素的数量,后跟:;这需要被过滤掉,以及#所有值的前缀。

"${arr[@]#*: ?}" >&/dev/null

这是一个参数替换,首先扩展 的值arr并过滤前面的所有内容,直到并包括'4: '( ),丢弃和 的#*: ?结果。这也可以抑制由于缺少结果变量分配而导致的错误。stdoutstderr

>&是 的缩写形式2>&1,将stderr输出 ( fd2) 分配给stdout( fd1) 并将两者都分配给/dev/null

现在的技巧是,使用 bash 的特殊下划线_参数,它将上一个命令中丢弃的结果参数(上面的第一个参数扩展操作和结果重定向到/dev/null)复制到我们的第二个参数替换中,其中#值前缀被过滤:

san="${_//#/}"

输出:

原:4:#1#2#3#4

已消毒:1 2 3 4

另请参阅:特殊参数和 shell 变量

答案4

在 Linux 上,您可以通过管道输入 source 命令:

echo $(xclip -o -selection clipboard)/copi/knott|source /dev/stdin

对于没有 的系统/dev/stdin,您必须先写入临时文件,然后再写入source它。

相关内容