Can ${var} parameter expansion expressions be nested in bash?

Viewed 53733

What I have is this:

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

Can this be nested (or not) into one line, i.e. a single expression?

I'm trying to strip the path and extension off of a script name so that only the base name is left. The above two lines work fine. My 'C' nature is simply driving me to obfuscate these even more.

15 Answers

If by nest, you mean something like this:

#!/bin/bash

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

echo ${${HELLO}WORLD}

Then no, you can't nest ${var} expressions. The bash syntax expander won't understand it.

However, if I understand your problem right, you might look at using the basename command - it strips the path from a given filename, and if given the extension, will strip that also. For example, running basename /some/path/to/script.sh .sh will return script.

This nesting does not appear to be possible in bash, but it works in zsh:

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

As there is already a lot of answer there, I just want to present two different ways for doing both: nesting parameter expansion and variable name manipulation. (So you will find four different answer there:).

Parameter expansion not really nested, but done in one line:

Without semicolon (;) nor newline:

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

Another way: you could use a fork to basename

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

This will make the job.

About concatenating variable name

If you want to construct varname, you could

use indirect expansion

foobar="baz"
varname="foo"
varname+="bar"
echo ${!varname}
baz

or use nameref

foobar="baz"
bar="foo"
declare -n reffoobar=${bar}bar
echo $reffoobar
baz

It will work if you follow the bellow shown way of taking on intermediate step :

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

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

The basename bultin could help with this, since you're specifically splitting on / in one part:

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

It's not really faster than the two line variant, but it is just one line using built-in functionality. Or, use zsh/ksh which can do the pattern nesting thing. :)

Though this is a very old thread, this device is ideal for either directly or randomly selecting a file/directory for processing (playing tunes, picking a film to watch or book to read, etc).

In bash I believe it is generally true that you cannot directly nest any two expansions of the same type, but if you can separate them with some different kind of expansion, it can be done.

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

Explanation: e is an array of directory names, c the selected directory, either named explicitly as $2,

${2:-...}  

where ... is the alternative random selection given by

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

where the

$((RANDOM%...))  

number generated by bash is divided by the number of items in array e, given by

${#e[@]}  

yielding the remainder (from the % operator) that becomes the index to array e

${e[...]}

Thus you have four nested expansions.

Related