Variable Mangling in Bash with String Operators
Editor's Note: This article has been updated by its author. Thank you, Pat.
Have you ever wanted to change the names of many files at once? Or, have you ever needed to use a default value for a variable that has no value? These and many other options are available to you when you use string operators in bash and other Bourne-derived shells.
String operators allow you to manipulate the contents of a variable without having to write your own shell functions to do so. They are provided through "curly brace" syntax. Any variable can be displayed as ${foo} without changing its meaning. This functionality often is used to protect a variable name from surrounding characters.
$ export foo=foo
$ echo ${foo}bar # foo exists so this works as expected
foobar
$ echo $foobar # foobar doesn't exist, so this doesn't
$
By the end of this article, you'll be able to use it for a whole lot more.
Three kinds of variable substitution are available for use: pattern matching, substitution and command substitution. I talk about the first two variables here and leave command substitution for another time.
In pattern matching, you can match from the left or from the right. The operators, along with their functions and examples, are shown below:
Operator: ${foo#t*is}
Function: deletes the shortest possible match from the left
Example:
$ export foo="this is a test"
$ echo ${foo#t*is}
is a test
$
Operator: ${foo##t*is}
Function: deletes the longest possible match from the left
Example:
$ export foo="this is a test"
$ echo ${foo##t*is}
a test
$
Operator: ${foo%t*st}
Function: deletes the shortest possible match from the right
Example:
$ export foo="this is a test"
$ echo ${foo%t*st}
this is a
$
Operator: ${foo%%t*st}
Function: deletes the longest possible match from the right
Example:
$ export foo="this is a test"
$ echo ${foo%%t*st}
$
Although the # and % identifiers may not seem obvious, they have a convenient mnemonic. The # key is on the left side of the $ key and operates from the left. The % key is on the right of the $ key and operates from the right. (This is true, at least, for US qwerty keyboards.)
The operators listed above can be used to do a variety of things. For example, the following script changes the extension of all .html files so they now are .htm files.
#!/bin/bash
# quickly convert html filenames for use on a dossy system
# only handles file extensions, not file names
for i in *.html; do
if [ -f ${i%l} ]; then
echo "${i%l} already exists"
else
mv $i ${i%l}
fi
done
Another kind of variable mangling you might want to employ is substitution. Four substitution operators are used in Bash, and they are shown below:
Operator: ${foo:-bar}
Function: If $foo exists and is not null, return $foo. If it doesn't exist or is null, return bar.
Example:
$ export foo=""
$ echo ${foo:-one}
one
$ echo $foo
$
Operator: ${foo:=bar}
Function: If $foo exists and is not null, return $foo. If it doesn't exist or is null, set $foo to bar and return bar.
Example:
$ export foo=""
$ echo ${foo:=one}
one
$ echo $foo
one
$
Operator: ${foo:+bar}
Function: If $foo exists and is not null, return bar. If it doesn't exist or is null, return a null.
Example:
$ export foo="this is a test"
$ echo ${foo:+bar}
bar
$
Operator: ${foo:?"error message"}
Function: If $foo exists and isn't null, return its value. If it doesn't exist or is null, print the error message. If no error message is given, it prints parameter null or not set. In a non-interactive shell, this aborts the current script. In an interactive shell, this simply prints the error message.
Example:
$ export foo="one"
$ for i in foo bar baz; do
> eval echo \${$i:?}
> done
one
bash: bar: parameter null or not set
bash: baz: parameter null or not set
$
The : in the above operators can be omitted. Doing so changes the behavior of the operator so that it simply tests for the existence of the variable. This, in turn, causes the creation of a variable, for example:
$ export foo="this is a test"
$ echo $bar
$ echo ${foo=bar}
this is a test
$ echo ${bar=bar}
bar
$ echo $bar
bar
$
These operators can be used in a variety of ways. A good example would be, in the case when no arguments are given, to give a default value to a variable normally read from command-line arguments. This example is demonstrated in the following script:
#!/bin/bash
export INFILE=${1-"infile"}
export OUTFILE=${2-"outfile"}
cat $INFILE > $OUTFILE
Copyright (c) 2005, 2000 by Pat Eyler. Originally published in Linux Gazette issue 57. Copyright (c) 2000, Specialized Systems Consultants, Inc. The material in this article may be distributed only subject to the terms and conditions set forth in the Open Publication License, v1.0 or later.
-- -pate http://on-ruby.blogspot.com
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Dynamic DNS—an Object Lesson in Problem Solving | May 21, 2013 |
| Using Salt Stack and Vagrant for Drupal Development | May 20, 2013 |
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
- RSS Feeds
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- Dynamic DNS—an Object Lesson in Problem Solving
- New Products
- Validate an E-Mail Address with PHP, the Right Way
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Download the Free Red Hat White Paper "Using an Open Source Framework to Catch the Bad Guy"
- Tech Tip: Really Simple HTTP Server with Python
- Roll your own dynamic dns
36 min 29 sec ago - Please correct the URL for Salt Stack's web site
3 hours 47 min ago - Android is Linux -- why no better inter-operation
6 hours 3 min ago - Connecting Android device to desktop Linux via USB
6 hours 31 min ago - Find new cell phone and tablet pc
7 hours 29 min ago - Epistle
8 hours 58 min ago - Automatically updating Guest Additions
10 hours 7 min ago - I like your topic on android
10 hours 53 min ago - This is the easiest tutorial
17 hours 29 min ago - Ahh, the Koolaid.
23 hours 7 min ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?



Comments
String operations the oo-way in bash
Try oobash. It is an oo-style string library for bash 4. It has support for german umlauts. It is written in bash. Many functions are available: -base64Decode, -base64Encode, -capitalize, -center, -charAt, -concat, -contains, -count, -endsWith, -equals, -equalsIgnoreCase, -reverse, -hashCode, -indexOf, -isAlnum, -isAlpha, -isAscii, -isDigit, -isEmpty, -isHexDigit, -isLowerCase, -isSpace, -isPrintable, -isUpperCase, -isVisible, -lastIndexOf, -length, -matches, -replaceAll, -replaceFirst, -startsWith, -substring, -swapCase, -toLowerCase, -toString, -toUpperCase, -trim, and -zfill.
Look at the contains example:
Call the constructor:
[Desktop]$ String a testXccc
Now use object.method:
[Desktop]$ a.contains tX
true
[Desktop]$ a.contains XtX
false
http://sourceforge.net/projects/oobash/
bash string replacment with null character or new line character
I'm having trouble with the string replacement function in bash. The problem is that I want to replace a printing character, in this case &, by a non-printing character, either new line or null in this case. I don't see how to specify the non-printing character in the string replacement function ${variable//a/b}.
I have a long, URL-encoded-like file name that I would like to parse with grep. I have used & as a delimiter between variables within the long file name. I would like to use the string replacement function in bash to search for all instances of & and replace each one with either the null character or the new line character since grep can recognize either one.
How do I specify a non-printing character in the bash string replacement function ?
Thank you.
Special Syntax
Use the $'\xNN' syntax for the non-printing character. Note though that a NULL character does not work:
$ cat j.sh v="hello=yes&world=no" v2=${v/&/$'\x0a'} # ^^^^^^^ change to newline echo -n ">>$v2<<" | hexdump -C v2=${v/&/$'\x00'} # ^^^^^^^ change to null (doesn't work) echo -n ">>$v2<<" | hexdump -CIf you run this you can see that the substitution works for a newline but not for a NULL:
Mitch Frazier is an Associate Editor for Linux Journal.
Multiple operations?
Very interesting article.
A question: is it possible to use in the same expression many operators, as:
${foo#t*is%t*st} which uses both '#t*is' and '%t*st' which gives 'is a' in the example?
I tried some forms but it doesn't work... Has someone an idea?
Doesn't Work
You can't do multiple operations in one expression.
Mitch Frazier is an Associate Editor for Linux Journal.
Variable
I want to do something like this using linux bash script:
a1="Chris Alonso"
i="1"
echo $a$i #I only trying to write: echo $a1 using the variable i
Someone can help me, please?
Eval
Eval will do this for you but you may decide you really don't want to do this after seeing it:
A slightly less complicated sequence would be something like:
It looks like what you're trying to do here is simulate arrays. If that's the case then you'd be better or using bash's built-in arrays.
Mitch Frazier is an Associate Editor for Linux Journal.
How about v=a$i echo ${!v}
How about
v=a$i echo ${!v}simplification of indirect reference
Is there any way to rid the statements of the variable assignment? As in, make it so that:
echo ${!a$i}
works? I'm thinking that there has to be a way to escape the "a$i" inside the indirect reference construct. I have a case where I'm trying to do this with the result of a regex match, and am not able to figure out the right syntax:
SRC_FOLDER=/var/website needleA_FOLDER=/var/www needleB_FOLDER=/var/htdocs for item in ${ARRAY[@]; do [[ "$item" =~ hay(needle)stack ]] && DIR=${!${BASH_REMATCH[1]}_FOLDER}; cp -R $SRC_FOLDER/* $DIR; done;But the seventh line (with the indirect reference) chokes with a "bad substitution" error. I should be able to do this on one line, without using eval with the right syntax, no?
Sincerely,
Tyler
Yes
That works and is simpler than my solution.
Mitch Frazier is an Associate Editor for Linux Journal.
: or not to :
Interesting, ":" can be ommited for "numeric" variables (script/function arguments).
baz=${foo:-bar}vs.
baz=${1-bar}First time I thought it is a typo, but it is not.
interesting, this will save a few seds and greps!
Interesting article, this will save me a few seds, greps and awks!
what if we are to operate on
what if we are to operate on the param $1 $2, ...?
i mean is it feasible to see a result of ${4%/*}
to get a valule as from
$ export $1="this is a test/none"
$ echo ${$1/*}
> this is a test
$ echo ${$2#*/}
> none
?
thanks
variable contents confusing
Minor typos not withstanding, I had a bit of a problem with the values of the variables used. assigning the value 'bar' to the variable bar makes it confusing to quickly figure out which is which. (Is that 'bar' another variable? Or a value?)
I would suggest making the simple change of putting your values in uppercase. They would stand out and make the article more readable.
For example:
$ export foo=foo
$ echo ${foo}bar # foo exists so this works as expected
foobar
$ echo $foobar # foobar doesn't exist, so this doesn't
$
becomes
$ export foo=FOO
$ echo ${foo}bar # foo exists so this works as expected
FOObar
$ echo $foobar # foobar doesn't exist, so this doesn't
$
You can see how the 'FOObar' on the third line becomes differentiated from the 'foobar' on the fourth line.
foo, bar, +foobar: worst things for programming since Microsoft.
Using foo and bar to try to inform someone is pretty much uniformly bad everywhere it's done, as foo and bar explicitly indicate things that don't have any meaning whatsoever. Foobar is even worse, since it is visibly only different from foo bar because of a single " " (space).
If you're trying to confuse the reader, use foo, bar, and especially foobar.
If you're trying to be informative, please, give your damn variables a short but logically useful name.
Part II?
OK but there's a lot more to it than just this. How about some of the following?
$(var:pos[:len]) # extract substr from pos (0-based) for len
$(var/substr/repl) # replace first match
$(var//substr/repl) # replace all matches
$(var/#substr/repl) # replace if matches at beginning (non-greedy)
$(var/##substr/repl) # replace if matches at beginning (greedy)
$(var/%substr/repl) # replace if matches at end (non-greedy)
$(var/%%substr/repl) # replace if matches at end (greedy)
${#var} # returns length of $var
${!var} # indirect expansion
...Sorry, those round parens
...Sorry, those round parens should be curlies.
Interesting
I think they author did a great job explaining the article and am glad that I was able to learn from it and finally found something interesting to read online!
Examples in Table 1 are rubbish
In addition to the incorrect $foo= (should be foo=), the last two examples don't illustrate the use of the construct they are supposed to . Pity the author did not proof-read the first table.
Examples using same operator yet differing results?!
${foo#t*is} deletes the shortest possible match from the left export $foo="this is a test" echo ${foo#t*is} is a test
fine, but next in line is supposed to remove the maximum from the left, but uses the same exact operator, how does it get the correct result?
${foo##t*is} deletes the longest possible match from the left export $foo="this is a test" echo ${foo#t*is} a test
Get's worse when going from the right, the original operation from the right is employed. Moreover, on my system an Ubuntu 05.10 descktop, this gave:
txtedmacs@phpserver:~$ export $foo="this is a test"
bash: export: `=this is a test': not a valid identifier
Take out the $foo, and it works fine.
Much easier to catch someone else's errors than one's own - I hate looking at my articles or emails.
Errors in article
As really-txtedmacs tried to politely point out, there are errors in the Pattern Matching table - Example column, as of when he looked at it and as of now. Each instance of "export $foo" should be "export foo" in bash and most similar shells. Also, the operator in the echo command needs to match exactly the operator in the first column. Interestingly, some but not all of these errors still exist in the original article at http://linuxgazette.net/issue57/eyler.html, which is in issue 57, not 67.
Otherwise, a very good article. I will save the info in my bag of tricks.
You're channelling Larry Wall, dude!
In Bourne shell and its ilk (like Korn shell and bash) the assignment syntax is:
foo=...You only prefix a variable's name with $ when you're "dereferencing" it (expanding it into its value).So the shell was parsing
export $foo="this is a test"as:export ???="this is a test"(where ??? is whatever "foo" was set to before this statement ... probably the empty string if the variable was previously unset).I know this is confusing because Perl does it completely differently. In Perl the $ is a "sigil" which, on an "lvalue" (a variable name or other assignable token) tells the interpeter what "type" of assignment is occuring. Thus a Perl statement like:
$foo="this is a test";(note the required semicolon, too) is a "scalar" assignment. This also sets the context of the assignment. In Perl a scalar value in scalar context is conceptually the closest to a normal shell variable assignment. However, a list value in a scalar assignment context is a different beast entirely. So a line of Perl likeperl -e '@bar=(1,2,3)]; $foo=@bar; print $foo ;'will set $foo to the number of items in the bar array. (Of course we could use @foo for the array name since they are different namespaces in Perl. But I wanted my example to be clear). So an array/list value in scalar context returns an integer (a type of scalar) which represents the number of elements in the list.Anyway, just remembrer that the shell $ it more like the C programming * operator ... it dereferences the variable into its value.
USA <> World :-)
Although the # and % identifiers may not seem obvious, they have a convenient mnemonic. The # key is on the left side of the $ key and operates from the left.
In the USA, perhaps, but my UK keyboard has the # key nestling up against the Enter and right-Shift keys. Not to mention layouts such as Dvorak...!
Other (non-USA-specific?!) Mnemonics
Another way to keep track is that we say "#1" and "1%", not "1#" and "%1". That is, unless you're using "#" to mean "pounds", in which case "1#" is correct, but it's antiquated at best in the USA, and presumably a nonissue for other countries that use metric...
C programmers are used to using "#" at the start of lines (#define, #include). LaTeX authors are used to "%" at the end of lines when writing macro definitions, as a comment to keep extraneous whitespace from creeping in--but "%" is comment to end-of-line so it's also likely to show up at the start of a line too...
Mnemonics
Thanks for sharing those mnemonic insights, it's most helpful.