Bash tricks you didn't usually use
After some experience with Bash programming (and after lots of script I’ve read and refactored) I’ve learned and figured out some useful tricks that may make code much more clean and pretty. Most of things here are in Bash manual, I’m just writing practical cases.
This article may be extended and/or rewritten in future.
Common tricks
Setting defaults for variables
Tired writing [[ "$variable" ]] || variable="default"
? Look here!
: "${VARIABLE:=default value}"
: "${VARIABLE:=$(command to get default)}"
Explaination:
:
- “empty” command. It passes all arguments to Bash interpreter, but does nothing."..."
- to prevent bugs/attacks. Just in case.${VARIABLE:=defaultvalue}
- if variableVARIABLE
is not set, set its value todefaultvalue
.
Reading data with separators correctly
Assume you need to parse a string with separators (maybe non-spaces). Use this:
IFS='_' read -a INPUTVALUES <<<"input_string_with_underscores_as_separators"
You’ll get a Bash array named INPUTVALUES
with all needed data parsed.
Reading newline-separated output
If you need to read newline-separated output of some command, don’t use while
- it’s slow. Look at this:
INPUTDATA=( $(command) )
INPUTDATA
will contain all output strings from your command. Note spaces near brackets - they indicate that it’s an array.
Fancy array output
Imagine you have an array like:
RESULT=( "my first string" "second one" )
You need it to be output, say, coma-separated. You start thinking about something like for i in ${RESULT[@]}; do
. But there’s a simpler solution!
(IFS=','; echo "${RESULT[*]}")
Note that using [@]
instead of [*]
will ignore IFS
and output a space-separated list. It’s because [@]
returns strings list, and [*]
generates a single string with IFS
as a separator.
ANSI C quoting
If you need to get “Tab” character or Unicode special character in a string, or just pass newlines as \n
, use following form:
echo $'My cat\nIs mad'
This will write:
My cat
Is mad
Be careful: $'...'
and $"..."
are different. First one is for ANSI C quoting, and second is used to translate your string to current locale.
Setting variables in while
loop and inside subprocesses
Everyone has this issue:
KIND='awful'
echo 'My String Is Awesome' | while read w; do
KIND="$w"
done
echo "KIND=${KIND}"
# KIND='awful'
# WTF???
In this case you have a pipeline (|
). Pipelines are not just I/O redirections, they cause Bash to create a new “subprocess” for while
loop (or any other builtin). Variable KIND
is not marked as exported by default (and you usually don’t want to export it), so Bash honestly sets it in subprocess, but doesn’t pass to parent process.
How to solve:
KIND='awful'
while read w; do
KIND="$w"
done < <(echo 'My String Is Awesome')
echo "KIND=${KIND}"
# KIND=Awesome
Here we use input redirection, which does not cause subprocess creation for while
.
Some more tricks with variables
V='My=var=Data=123'
Cutting variable value without cut
:
echo "${V#*=}" # Remove first occurence of template (after #) from the beginning of the string
# var=Data=123
echo "${V##*=}" # Remove all occurencies of template (after #) from the beginning of the string
# 123
echo "${V%=*}" # Same as # for string's end
# My=var=Data
echo "${V%%=*}" # Same as ## from strings end
# My
Pattern substitutions
V='My=var=Data=123'
echo "${V/=/,}"
# My,var=Data=123
echo "${V//=/,}"
# My,var,Data,123
echo "${V/#My/Their}" # Match at the beginning
# Their=var=Data=123
echo "${V/%123/456}" # Match at the beginning
# My=var=Data=456
echo "${V/%123}" # Remove pattern
# My=var=Data=
echo "${V/%=*}" # Wildcard!
# My
Same with arrays:
V=( 'My=Data=123' 'Their=Data=123' )
echo "${V[@]/%123/456}"
Upper and lower case:
echo "${V^}" # First char upper case
echo "${V^^}" # all chars
echo "${V,}" # First lower
echo "${V,,}" # all lower
echo "${V^t}" # all previous commands may be used with pattern, it will be checked and only matching characters will be transformed
Get all variables matching prefix
Yes, Bash can do it!
MY_A='foo'
MY_B='bar'
VAR2='baz'
echo "${!MY_@}"
# MY_A MY_B
( IFS=','; echo "${!MY_*}" )
# MY_A,MY_B
Regular expressions
Bash support extended regexps in expressions:
[[ "--o=v" =~ ^\-\-[a-z]+= ]]
echo $?
# 0
Functions and arguments
Fancy long arguments handling
To handle long options with option and value separated by =
, like: --start=100 --end=1000 --path=/data
:
local -A OPTIONS
while [[ $# -gt 0 ]] && [[ "x${1:0:2}" =~ 'x--' ]]; do
local _opt="${1%%=*}"
OPTIONS["${_opt#--}"]="${1#*=}"
[[ "${OPTIONS["${_opt#--}"]}" == "${_opt}" ]] && OPTIONS["${_opt#--}"]='true'
shift
done
For options separated by space:
local -A OPTIONS
while [[ $# -gt 0 ]] && [[ "x${1:0:2}" =~ 'x--' ]]; do
if [[ -z "$2" ]] || [[ "x${2:0:2}" =~ 'x--' ]]; then
OPTIONS["${1#--}"]="true"
shift
else
OPTIONS["${1#--}"]="$2"
shift 2
fi
done
After both option handlers you’ll get:
OPTIONS
associative array where indexes are option names and values are their values supplied- If option had no value supplied, it’s treated as boolean and set to string
true
. - All arguments that are not handled, are in
$@
.
I’d recommend to use first algorithm because it strictly defines arguments and options.
blog comments powered by Disqus
Published
Category
scriptsTags
2020
August
- August 11, 2020 » Identifying AWS EBS volumes on instance
June
- June 24, 2020 » AWS S3 Website with private-only access
2018
September
- September 4, 2018 » Multiple ways of PowerShell templating
2017
September
- September 17, 2017 » Link: RDP URI scheme
August
- August 15, 2017 » Link: SSL settings and checks
- August 15, 2017 » Link: Template files substituter for Docker
- August 8, 2017 » Link: Classes in PowerShell
April
- April 15, 2017 » Bash tricks you didn't usually use
2016
August
- August 30, 2016 » Configuring RancherOS for use with AWS autoscaling + Swarm cluster
April
- April 26, 2016 » Building JS assets with MSBuild
- April 25, 2016 » Applying Web.config transforms to all config files
2015
August
- August 29, 2015 » Check if identifier is declared in Bash
2014
April
- April 2, 2014 » PID file management in Bash
- April 2, 2014 » Logging routine for Bash
- April 2, 2014 » Lock file management in Bash