Bash Parameter Expansion

October 1st, 2008 by Mitch Frazier in

Your rating: None Average: 4.3 (24 votes)

If you use bash you already know what Parameter Expansion is, although you may have used it without knowing its name. Anytime you use a dollar sign followed by a variable name you're doing what bash calls Parameter expansion, eg echo $a or a=$b. But parameter expansion has numerous other forms which allow you to expand a parameter and modify the value or substitute other values in the expansion process.

Parameter expansion comes in many forms in bash, the simplest is just a dollar sign followed by a name, eg $a. This form merely substitutes the value of the variable in place of the parameter expansion expression. The variable name can also optionally be surround by braces, eg ${a}. If the variable name is immediately followed by characters that could be part of a variable name then the braces are needed to delimit the variable name, for example if you remove the braces from echo ${a}bc bash will try to expand the variable "abc" rather than "a".

One useful form of parameter expansion is to use a default value for a variable if it is not set. This is done with the syntax: ${VAR:-DFLT}. You might use this to allow your code to be modified via variables from the environment. Consider the following from a script, call it test.sh:

  TEST_MODE=${TEST_MODE:-0}
  ...
  if [[ $TEST_MODE -eq 0 ]]; then
      echo "Running in live mode"
  else
      echo "Running in test mode"
  fi
Normally the script runs in "live" mode but if you run it via:
  $ env TEST_MODE=1 sh test.sh
it runs in test mode.

You might also use the default value expansion with command line arguments or values from a config file, for example:

  # set cmd_param_x to 1 if seen on the command line
  ...
  if [[ ${cmd_param_x:-0} -eq 0 ]]; then
      echo "-x not specified"
  else
      echo "-x specified"
  fi

Another useful form of parameter expansion is to expand a variable and do string substitution on the value using the form ${VAR/search/replace}. For example:

  VAR=aabbcc
  echo ${VAR/b/-dd-}
outputs "aa-dd-bcc". Note that only the first instance of the search string is replaced, if you want to replace all instances use a double slash:
  VAR=aabbcc
  echo ${VAR//b/-dd-}
which now outputs "aa-dd--dd-cc".

There are also expansions for removing prefixes and suffixes. The form ${VAR#pattern} removes any prefix from the expanded value that matches the pattern. The removed prefix is the shortest matching prefix, if you use double pound-signs/hash-marks the longest matching prefix is removed. Similarily, the form ${VAR%pattern} removes a matching suffix (single percent for the shortest suffix, double for the longest). For example:

  file=data.txt
  echo ${file%.*}
  echo ${file#*.}
outputs the file base and extension respectively ("data" and "txt").

Note: if you have trouble remembering which is which of these two syntaxes, the "#" is to the left of the "%" key on your keyboard, just as prefixes come before suffixes. Also note that these are glob patterns not regular expressions.

Another expansion that exists is to extract substrings from the expanded value using the form ${VAR:offset:length}. This works in the expected form: offsets start at zero, if you don't specify a length it goes to the end of the string. For example:

  str=abcdefgh
  echo ${str:0:1}
  echo ${str:1}
outputs "a" and "bcdefgh".

This form also accepts negative offsets which count backwards from the end of the string. So this:

  str=abcdefgh
  echo ${str:-3:2}
produces "abcdefgh"... oops, what happened there? What happened was that bash misinterpretted what we wanted because the expansion looks like a default value expansion: ${VAR:-DFLT}. First time I tried this I stared at it for quite a while before a light came on as to how to do it (without using a variable [see below]):
  str=abcdefgh
  echo ${str:$((-3)):2}
which outputs the desired value "fg". The "$((...))" causes bash to treat the value as an arithmetic expansion (ie a number). Another slightly longer way of doing this is:
  str=abcdefgh
  i=-3
  echo ${str:$i:2}

The final form of parameter expansion I want to mention is one which simply expands to the length of the variable's value, its form is ${#VAR}. So for example:

  str=abcdef
  echo ${#str}
outputs "6".

Using these forms of parameter expansion in your shell scripts can simplify and shorten your scripts. These are not the only forms of parameter expansion that bash supports but they're the ones that I've found most useful over time. For more information see the "Parameter Expansion" section of the bash man page.

p.s. Note that all of the above forms of parameter expansion also work with bash's Special parameters: "$$", "$0", "$1", etc.

__________________________

Mitch Frazier is an Associate Editor for Linux Journal and the Web Editor for linuxjournal.com.


Special Magazine Offer -- Free Gift with Subscription
Receive a free digital copy of Linux Journal's System Administration Special Edition as well as instant online access to current and past issues. CLICK HERE for offer

Linux Journal: delivering readers the advice and inspiration they need to get the most out of their Linux systems since 1994.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
profylno's picture

Multiple Operations?

On October 19th, 2008 profylno (not verified) says:

I've wanted to know for a long time if it is possible to use a combination of these operations, for example, to make two substitutions. I find it so annoying when I still have to use sed for a second substitution after a nice little bash expansion.

Mitch Frazier's picture

No

On October 23rd, 2008 Mitch Frazier says:

You mean something like:

  a='hello world'
  a=${${a/hello/goodbye}/world/earth}   # NO GOOD

That doesn't work, because bash expects a variable name after "${" not a value. Although I'm not sure why you would use sed instead of just using a second bash substitution, eg:

  a='hello world'
  a=${a/hello/goodbye}
  a=${a/world/earth}

But maybe I'm misunderstanding your question.

__________________________

Mitch Frazier is an Associate Editor for Linux Journal and the Web Editor for linuxjournal.com.

profylno's picture

You understand my question

On November 1st, 2008 profylno (not verified) says:

You understand my question perfectly.
As for your suggestion:
Suppose I have a script that takes an argument that will always be of the form CRAPimportantstuff, and I wanted to use it just once, I could say

  blablabla ${1#CRAP}

I wouldn't have to set somevar to ${1#CRAP} and then say 'blablabla $somevar', which is something I like very much about bash parameter expansion. But if the argument was in the form CRAPimportanstuffCRAP, this wouldn't fly. Now, I could, as you suggested, introduce another variable (or just change $1, though I'm not sure if that's possible), but I usually just end up doing

  blablabla `echo -n ${1#CRAP} | sed 's/CRAP$//'`

or something similarly ugly, because I don't feel like setting a variable to a value I'm only going to use once. Perhaps this is a bad habit on my part, and I should just do as you suggest. I just find it annoying that there's nothing that can handle the second case in a way as pretty and compact as the first (or, as was my original question, is there?).

Mitch Frazier's picture

Don't think so

On November 4th, 2008 Mitch Frazier says:

No easy way to do that comes to mind.

__________________________

Mitch Frazier is an Associate Editor for Linux Journal and the Web Editor for linuxjournal.com.

peter.green's picture

Keyboard-based mnemonics...

On October 8th, 2008 peter.green says:

...don't travel well, as I've pointed out before on LJ. My UK keyboard has the # way over to the right of the % (and two rows down!)

Off the top of my head, a less US-centric reminder might be the well-known game Tic-Tac-Toe, aka Noughts-and-Crosses. The # resembles the game grid and the % the noughts: in play, the grid comes first, then the noughts.

Edit: Having slept on it, an even simpler mnemonic (for English-speakers) is that alphabetically, H for Hash comes before P for Percent.

Gnusci's picture

Nice tips...

On October 3rd, 2008 Gnusci (not verified) says:

This tips will really help me a lot, much simpler than other ways, Thanks

flatcap's picture

Prefix / Suffix example

On October 3rd, 2008 flatcap (not verified) says:

You might want to use the double # format if there could be more than one '.' in the filename.

FILE=some.data.txt

echo ${FILE%.*}       # "some.data"
echo ${FILE#*.}       # "data.txt"

echo ${FILE%.*}       # "some.data"
echo ${FILE##*.}      # "txt"
Boscorama's picture

Negative indices....

On October 2nd, 2008 Boscorama (not verified) says:

In the 'trailing' sub-string example, just place a space before the negative number:

str=abcdefgh
echo ${str: -3:2}

outputs "fg" as desired.

wodny's picture

or echo ${str:(-3):2}

On October 18th, 2008 wodny (not verified) says:

or echo ${str:(-3):2}

Mitch Frazier's picture

Much simpler than my solution

On October 3rd, 2008 Mitch Frazier says:

Thanks

__________________________

Mitch Frazier is an Associate Editor for Linux Journal and the Web Editor for linuxjournal.com.

Fahd Shariff's picture

Very useful tips! Now I

On October 2nd, 2008 Fahd Shariff (not verified) says:

Very useful tips!
Now I won't have to echo my variables into sed or awk anymore!

Post new comment

Please note that comments may not appear immediately, so there is no need to repost your comment.
The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <pre> <ul> <ol> <li> <dl> <dt> <dd> <i> <b>
  • Lines and paragraphs break automatically.

More information about formatting options

Newsletter

Each week Linux Journal editors will tell you what's hot in the world of Linux. You will receive late breaking news, technical tips and tricks, and links to in-depth stories featured on www.linuxjournal.com.
Sign up for our Email Newsletter

Tech Tip Videos

From the Magazine

December 2009, #188

If last month's Infrastrucuture issue was too "big" for you then try on this month's Embedded issue. Find out how to use Player for programming mobile robots, build a humidity controller for your root cellar, find out how to reduce the boot time of your embedded system, and if you're new to embedded systems find out the basics that go into one. You can also read about the Beagle Board, the Mesh Potato and a spate of other interestingly named items. And along with our regular columns don't miss our new monthly column: Economy Size Geek.







Read this issue