$ {var}参数扩展表达式可以嵌套在bash中吗?


70

我所拥有的是:

progname=${0%.*}
progname=${progname##*/}

可以将它嵌套(或不嵌套)到一行,即一个表达式中吗?

我正在尝试删除脚本名称的路径和扩展名,以便仅保留基本名称。以上两行工作正常。我的“ C”本质只是在驱使我更加混淆这些。

Answers:


57

如果是嵌套的,您的意思是这样的:

#!/bin/bash

export HELLO="HELLO"
export HELLOWORLD="Hello, world!"

echo ${${HELLO}WORLD}

那不行,您不能嵌套${var}表达式。bash语法扩展器无法理解。

但是,如果我正确理解了您的问题,则可以考虑使用basename命令-它会删除给定文件名中的路径,如果提供了扩展名,也会删除该路径。例如,运行basename /some/path/to/script.sh .sh将返回script


如果您使用的是索引,可以吗?例如,ARR=('foo' 'bar' 'bogus'); i=0; while /bin/true ; do echo ${ARR[$i]} ; i=(( (i + 1) % 3 )); done它显然对代码没有用,但仅作为示例。
Ian Stapleton Cordasco 2013年

2
其实,这在某些情况下支持,至少在bashksh。这工作:x=yyy; y=xxxyyy; echo ${y%${x}}。我认为重要的一点是,嵌套扩展是其中一个运算符的参数。不过,我还没有看到它的记录真的很好。
twalberg 2013年

这对我不起作用,我不知所措:$ {$ {HELLO} WORLD}:替代品错误。有任何想法吗?我在上软呢帽。
卡洛斯·布里比斯卡斯

2
正如@Tim所说,@ CarlosBribiescas,“不,您不能嵌套表达式”。该示例echo ${${HELLO}WORLD}说明了这种可能性。
Atcold

60

Bash支持间接扩展:

$ FOO_BAR="foobar"
$ foo=FOO
$ foobar=${foo}_BAR
$ echo ${foobar}
FOO_BAR
$ echo ${!foobar}
foobar

这应该支持您要查找的嵌套。


2
嗨,什么!意味着在壳?这意味着在这里用foobar的值替换?
2014年

很好的例子。如果他们没有可用的间接参数扩展,在这种情况下该怎么办?
Nadim Hussami

这很好,谢谢!:)不幸的是,我打了一个小错字,使我对事实和谷歌的问题提出了一个小时的疑问,哈哈。
约翰·汉弗莱斯

18

以下选项对我有效:

NAME="par1-par2-par3"
echo $(TMP=${NAME%-*};echo ${TMP##*-})

输出为:

par2

2
这是最好的答案
Eben Geer

辉煌。我只想知道,为什么没有第二(内部)命令echo就将其视为par2命令?
AS

4
这将创建一个子外壳。参数扩展本身不会创建子外壳。
ceving 2007年

16

一个旧线程,但答案可能是使用间接寻址:$ {!PARAMETER}

例如,考虑以下几行:

H="abc"
PARAM="H"
echo ${!PARAM} #gives abc

7

在bash中似乎无法嵌套,但在zsh中可以使用:

progname=${${0%.*}##*/}

6

实际上,可以使用两个步骤在bash中创建嵌套变量。

这是一个基于Tim的帖子的测试脚本,使用了user1956358提出的想法。

#!/bin/bash
export HELLO="HELLO"
export HELLOWORLD="Hello, world!"

# This command does not work properly in bash
echo ${${HELLO}WORLD}

# However, a two-step process does work
export TEMP=${HELLO}WORLD
echo ${!TEMP}

输出为:

Hello, world!

通过从命令行运行“ info bash ”,然后搜索“ Shell参数扩展”,可以解释许多巧妙的技巧。 ”。我一直在阅读一些自己的今天,刚刚失去了约20分钟,我的一天,但我的脚本要更好地得到了很多...

更新:经过更多阅读后,我建议您根据最初的问题使用此替代方法。

progname=${0##*/}

它返回

bash

1
OP询问是否可以在bash中进行嵌套扩展,基本上您会回答(这是正确的):不,您需要将一个表达式扩展为一个临时变量,然后对该变量使用第二个表达式。但是,其他外壳程序也支持模式嵌套。
Christian Herenz

6

${${a}}这样的表达不起作用。要解决此问题,可以使用eval

b=value
a=b
eval aval=\$$a
echo $aval

输出为

value


3

OP的原始问题有1行的解决方案,即文件扩展名被删除的脚本的基本名称:

progname=$(tmp=${0%.*} ; echo ${tmp##*/})

这是另一个,但是使用作弊的基本名称:

progname=$(basename ${0%.*})

其他答案已经远离OP的原始问题,而集中在是否可以仅使用扩展表达式的结果,${!var}但是遇到了var必须明确匹配变量名的限制。话虽如此,如果将表达式与分号链接在一起,就不会阻止您获得1-liner答案。

ANIMAL=CAT
BABYCAT=KITTEN
tmp=BABY${ANIMAL} ; ANSWER=${!tmp} # ANSWER=KITTEN

如果要使它看起来像一条语句,则可以将其嵌套在子外壳中,即

ANSWER=$( tmp=BABY${ANIMAL) ; echo ${!tmp} ) # ANSWER=KITTEN

有趣的用法是对bash函数的参数进行间接处理。然后,您可以嵌套bash函数调用以实现多层嵌套间接寻址,因为我们可以执行嵌套命令:

这是一个表达式间接的演示:

deref() { echo ${!1} ; }
ANIMAL=CAT
BABYCAT=KITTEN
deref BABY${ANIMAL} # Outputs: KITTEN

这是通过嵌套命令进行多级间接定向的演示:

deref() { echo ${!1} ; }
export AA=BB
export BB=CC
export CC=Hiya
deref AA # Outputs: BB
deref $(deref AA) # Outputs: CC
deref $(deref $(deref AA)) # Outputs: Hiya

感谢您的间接解释!确实不幸的是,它只能用于引用现有的变量名。当然可以制作一个单行纸,但最终要涉及到一个临时变量的分配,这是我一直试图避免的事情。
史蒂文·卢

@ steven-lu我的deref()功能如何?如果已声明,则可以使用没有临时变量的oneliner?
史蒂芬·泉

从任何合理的角度来看,维护方面的困惑比使用临时变量要高2至3倍😉使我想起了C预处理程序的滥用
Steven Lu

2

我知道这是一条古老的线索,但这是我的2美分。

这是一个(必不可少的)bash函数,它允许所需的功能:

read_var() {
  set | grep ^$1\\b | sed s/^$1=//
}

这是一个简短的测试脚本:

#!/bin/bash

read_var() {
  set | grep ^$1\\b | sed s/^$1=//
}

FOO=12
BAR=34

ABC_VAR=FOO
DEF_VAR=BAR

for a in ABC DEF; do
  echo $a = $(read_var $(read_var ${a}_VAR))
done

输出如预期的那样:

ABC = 12
DEF = 34

我现在知道这是很老,但你可以做掉与grep和用sed -np为:set | sed -n s/^$1=//p。顺便说一句不错的黑客。
Dani_l

2

如果您按照下面显示的方式进行中间步骤,它将起作用:

export HELLO="HELLO"
export HELLOWORLD="Hello, world!"

varname=${HELLO}WORLD
echo ${!varname}

但不是由其他Shell共享的。不是我想要的,而是接近的东西。
理查德

1

基本名称bultin可以帮助解决此问题,因为您专门将/分为以下部分:

user@host# var=/path/to/file.extension
user@host# basename ${var%%.*}
file
user@host#

它实际上并没有比两行变体快,但是使用内置功能只是一行。或者,使用zsh / ksh可以进行模式嵌套。:)


抱歉,格式化。就试一试吧。:) var = $(basename $ {var %%。*})
dannysauer,2009年

或使用Tim所说的使用bash专用的基本名称扩展名删除,而我错过了...:D
dannysauer

1

尽管这是一个非常古老的线程,但该设备非常适合直接或随机选择文件/目录进行处理(播放音乐,挑选要观看的电影或阅读书籍等)。

在bash中,我相信通常不能直接嵌套相同类型的任何两个扩展,但是如果可以用某种不同类型的扩展将它们分开,则可以做到。

e=($(find . -maxdepth 1 -type d))
c=${2:-${e[$((RANDOM%${#e[@]}))]}}

说明:e是目录名称的数组,c是所选目录,其显式命名为$ 2,

${2:-...}  

其中...是由给出的替代随机选择

${e[$((RANDOM%${#e[@]}))]}  

在哪里

$((RANDOM%...))  

bash生成的数字除以数组e中的项数,由

${#e[@]}  

产生余数(来自%运算符),成为数组e的索引

${e[...]}

因此,您有四个嵌套的扩展。


0

如果动机是按照Python的“理解”精神“模糊处理”(我想说是简化的)数组处理,请创建一个按顺序执行操作的辅助函数。

function fixupnames()
   {
   pre=$1 ; suf=$2 ; shift ; shift ; args=($@)
   args=(${args[@]/#/${pre}-})
   args=(${args[@]/%/-${suf}})
   echo ${args[@]}
   }

您可以将结果与一个好的衬里一起使用。

$ echo $(fixupnames a b abc def ghi)
a-abc-b a-def-b a-ghi-b

0

没有嵌套,但是可以一行完成

不使用分号(;)或换行符:

progname=${0%.*} progname=${progname##*/}

另一种方式:您可以使用 basename

progname=$(basename ${0%.*})

-1

评估将使您能够做自己想做的事情:

export HELLO="HELLO"
export HELLOWORLD="Hello, world!"

eval echo "\${${HELLO}WORLD}"

输出: Hello, world

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.