SlideShare a Scribd company logo
Don’t BASH your head in:
Rx for shell variables.
Steven Lembark
Workhorse Computing
lembark@wrkhors.com
Quick review
BASH is interpreted.
Loops are re-parsed.
Variables can appear anywhere.
Unlike Perl, Python, Ruby, Go, Haskell, Scala, Scheme,
Which separate statements from var’s.
Basic Variables
Assignments to foo:
foo=’$files’; literal ‘$files’.
Basic Variables
Assignments to foo:
foo=’$files’; literal ‘$files’.
foo=”$files”; interpolated value of files.
Basic Variables
Assignments to foo:
foo=’$files’; literal ‘$files’.
foo=”$files”; interpolated value of files.
foo=$(ls $files); command output.
Basic Variables
Assignments to foo:
foo=’$files’; literal ‘$files’.
foo=”$files”; interpolated value of files.
foo=$(ls $files); command output.
foo=”$(ls $files)”; string with listing of files.
Basic Variables
Assignments to foo:
foo=’$files’; literal ‘$files’.
foo=”$files”; interpolated value of files.
foo=$(ls $files); command output.
Most of the work is interpolating:
echo “Your files are: $(ls $somedir)”;
De-mangling variable names
> foo=’bar’;
> echo “foo$foo”; ”foobar”
> echo “$bar_foo”; “”
Oops: Variable “bar_foo” doesn’t exist.
De-mangling variable names
> foo=’bar’;
> echo “foo$foo”; ”foobar”
> echo “$bar_foo”; “”
Isolate ‘foo’ as variable name:
“${foo}_bar # “bar_bar”
Variable commands
cmd=’/bin/ls’;
arg=’-lt’;
Variable commands
cmd=’/bin/ls’;
arg=’-lt’;
files=$($cmd $arg $1); /bin/ls -lt ...
Variable commands
# interpolate each command into the loop
for i in $cmd1 $cmd2 $cmd3
do
$i $args;
done
Really anywhere!
foo=’bar’;
Really, anywhere!
foo=’bar’;
$foo=’blort’;
Q: What happens?
Really, anywhere!
foo=’bar’;
$foo=’blort’;
Q: What happens?
A: Nada.
bash: bar=blort: command not found
Your one chance at success
BASH parses in two phases:
Lexical substitution & tokenizing.
Execution.
Variables have to expand on the first pass to be used.
“foo=blort” cannot be executed, so it failed.
A second chance in life
‘eval’ adds one cycle.
Interpolates variables.
Passes result to the shell.
‘++’ is two levels deep.
> eval"$foo=blort";
+ eval bar=blort
++ bar=blort
> echo $bar;
+ echo blort
blort
Or a third chance...
eval “eval … “;
Work out what is happening:
a=’$HOME/?.*’;
b=’foo’;
c=eval “eval $a $b”’;
Verbosity & Execution
See what bash is doing with the variables:
> set -vx;
Verbosity & Execution
See what bash is doing with the variables:
> set -vx;
echo -ne "033]0;./$(basename $PWD) 007"
+++ basename
/sandbox/lembark/writings/RockfordLUG/bash
++ echo -ne '033]0;./bash 007'
>
Verbosity & Execution
See what bash is doing with the variables:
> set -vx;
echo -ne "033]0;./$(basename $PWD) 007"
+++ basename
/sandbox/lembark/writings/RockfordLUG/bash
++ echo -ne '033]0;./bash 007'
>
“unset” removes variables
> unset PROMPT_COMMAND;
> set -vx;
>
Or set verbosity in the script
#!/bin/bash -vx
…
or localize the verbosity:
set -vx;
do something
set -;
Verbosity & Execution
> unset PROMPT_COMMAND;
> set -vx;
> foo=bar; what I typed
foo=bar; what BASH read
+ foo=bar single ‘+’is one level deep
Verbosity & Execution
> unset PROMPT_COMMAND;
> set -vx;
> foo=bar; what I typed
foo=bar; what BASH read
+ foo=bar single ‘+’is one level deep
> $foo='blort';
>foo='blort';
+ bar=blort no second chance to re-parse
Verbosity & Execution
> unset PROMPT_COMMAND;
> set -vx;
> foo=bar; what I typed
foo=bar; what BASH read
+ foo=bar single ‘+’is one level deep
> $foo='blort';
>foo='blort';
+ bar=blort no second chance to re-parse
bash: bar=blort: command not found
Why use ‘-vx’?
-v alone is useful sometimes.
-x alone usually isn’t.
Example: Hidden disk hogs
#!/bin/bash
dirs="/home/$(whoami)/.*";
cmd="du -msx $dirs | sort -rn | head -3";
eval "$cmd";
Buggy hogs
$ ./y
1232 /home/lembark/.
0 /home/lembark/..
Add verbosity
#!/bin/bash
set -vx;
dirs="/home/$(whoami)/.*";
cmd="du -msx $dirs | sort -rn | head -3";
eval "$cmd";
Shows the bug: . & ..
$ ./y
dirs="/home/$(whoami)/.*";
++ whoami
+ dirs='/home/lembark/.*'
cmd="du -msx $dirs | sort -rn | head -3";
+ cmd='du -msx /home/lembark/.* | sort -rn | head -3'
eval "$cmd";
+ eval 'du -msx /home/lembark/.* | sort -rn | head -3'
du -msx /home/lembark/.* | sort -rn | head -3
++ sort -rn
++ head -3
++ du -msx /home/lembark/. /home/lembark/..
/home/lembark/.aceinnovative /home/lembark/.adobe <snip>
1232 /home/lembark/.
0 /home/lembark/..
Fix: Add a letter
#!/bin/bash
set -vx;
dirs="/home/$(whoami)/.[a-z]*";
cmd="du -msx $dirs | sort -rn | head -3";
eval "$cmd";
Start with the right dirs
$ ./y
dirs="/home/$(whoami)/.[a-z]*";
++ whoami
+ dirs='/home/lembark/.[a-z]*'
cmd="du -msx $dirs | sort -rn | head -3";
+ cmd='du -msx /home/lembark/.[a-z]* | sort -rn | head -3'
eval "$cmd";
+ eval 'du -msx /home/lembark/.[a-z]* | sort -rn | head -3'
du -msx /home/lembark/.[a-z]* | sort -rn | head -3
++ sort -rn
++ head -3
++ du -msx /home/lembark/.aceinnovative /home/lembark/.adobe
<snip>
270 /home/lembark/.openoffice
244 /home/lembark/.mozilla
193 /home/lembark/.config
Compare: Only “-v”
$ ./y
dirs="/home/$(whoami)/.[a-z]*";
cmd="du -msx $dirs | sort -rn | head -3";
eval "$cmd";
du -msx /home/lembark/.[a-z]* | sort -rn | head -3
270 /home/lembark/.openoffice
244 /home/lembark/.mozilla
193 /home/lembark/.config
Compare: Only “-x”
$ ./y
++ whoami
+ dirs='/home/lembark/.[a-z]*'
+ cmd='du -msx /home/lembark/.[a-z]* | sort -rn | head -3'
+ eval 'du -msx /home/lembark/.[a-z]* | sort -rn | head -3'
++ sort -rn
++ head -3
++ du -msx /home/lembark/.aceinnovative /home/lembark/.adobe
<snip>
270 /home/lembark/.openoffice
244 /home/lembark/.mozilla
193 /home/lembark/.config
Command execution
We all remember backticks:
a=`ls -al ~`’;
Command execution
We all remember backticks:
a=`ls -al ~`’;
Better off forgotten:
No way to nest them for one.
Hard to read for another.
Command execution
BASH offers a better way:
$( ... )
i.e., “interpolate subshell output”.
Output of arbitrary commands:
files=$(ls ~);
jobs=$( grep ‘MHz’ /proc/cpuinfo | wc -l );
echo -e “DiskHogz:n$(du -msx *|sort -rn|head)”;
Twisting a path with basename
basename
locates
input for
next step.
cmd=’/image/bin/extract-hi-res’;
dir=’../raw’;
cd high-res || exit -1;
for i in ../low-res/culled/*;
do
echo “Input: ‘$i’”;
$cmd $dir/$(basename $i .ppm).nef;
done
Twisting a path with basename
Quotes
hilite
whitespace
in $1.
Don’t
leave
home
without
them...
cmd=’/image/bin/extract-hi-res’;
dir=’../raw’;
cd high-res || exit -1;
for i in ../low-res/culled/*;
do
echo “Input: ‘$i’”;
$cmd $dir/$(basename $i .ppm).nef;
done
Being there
A “here script” is “appended from stdin”.
Double-quotish.
> perl -MCPAN -E shell <<CPAN 2>&1 | tee a;
upgrade
install Module::FromPerlVer
q
CPAN
Being there
A “here script” is “appended from stdin”.
Double-quotish, into stdin.
> perl -MCPAN -E shell <<CPAN 2>&1 | tee a;
upgrade
install Module::FromPerlVer
q
CPAN
Being there
Closing tag sends EOF (^D) to command:
> perl -MCPAN -E shell <<CPAN 2>&1 | tee a;
upgrade
install Module::FromPerlVer
CPAN
Being there
module=’Module::FromPerlVer’;
> perl -MCPAN -E shell <<CPAN 2>&1 | tee a;
upgrade
install $module
CPAN
Being there
#!/bin/bash
...
path=”$mysql_d/$tspace”;
mkdir -p $path || exit -2;
mysql -U$user -P$pass <<SQL || exit -3;
create tablespace $tspace
using ‘$path’ … ;
create table big ( … ) tablespace $tspace;
SQL
Being there
mysql -U$user -P$pass <<SQL || exit -3;
create tablespace $tspace
using ‘$path’ … ;
create table
$(cat $table-1.sql)
tablespace $tspace;
SQL
Slicing with curlies
Remove strings from the head or tail of a string.
${i#glob} ${i%glob}
${i##glob} ${i%%glob}
Slicing with curlies
Slice the head:
${i#glob}
${i##glob}
# is shortest match
## is longest match
Slicing with curlies
Slice the tail:
${i%glob}
${i%%glob}
% is shortest match
%% is longest match
Stripping a prefix.
Say you want to prefix ‘/opt/bin’ onto a PATH.
But it may already be there.
You don’t know if someone else hacked the path.
Q: How can we put ‘/opt/bin’ at the front, once?
Stripping a prefix.
Say you want to prefix ‘/opt/bin’ onto a PATH.
But it may already be there.
You don’t know if someone else hacked the path.
Q: How can we put ‘/opt/bin’ at the front, once?
A: Take it off each time.
Striptease.
‘#’ strips off leading content.
Say we tried this:
PATH=”/opt/bin:${PATH#/opt/bin:}”;
OK, I can run it a hundred times.
Path hack striptease.
‘#’ strips off leading content.
Say we tried this:
PATH=”/opt/bin:${PATH#/opt/bin:}”;
OK, I can run it a hundred times.
Until /opt/bin isn’t first:
“~/bin:/opt/bin: ...”
Globs save the day
Find everything up to the first match:
PATH=”/opt/bin:${PATH#*/opt/bin:}”;
> echo $PATH;
/usr/local/bin:/usr/bin:/bin:/opt/bin:/usr/
i486-pc-linux-gnu/gcc-bin/4.1.2
Globs save the day
Find everything up to the first match:
PATH=”/opt/bin:${PATH#*/opt/bin:}”;
> echo ${PATH#*/opt/bin:};
+ echo /usr/local/bin:/usr/bin:/bin:/opt/bin:/
usr/i486-pc-linux-gnu/gcc-bin/4.1.2
Globs save the day
Find everything up to the first match:
PATH=”/opt/bin:${PATH#*/opt/bin:}”;
> echo ${PATH#*/opt/bin:};
+ echo /usr/local/bin:/usr/bin:/bin:/opt/bin:/
usr/i486-pc-linux-gnu/gcc-bin/4.1.2
Globs save the day
Find everything up to the first match:
PATH=”/opt/bin:${PATH#*/opt/bin:}”;
/usr/i486-pc-linux-gnu/gcc-bin/4.1.2
Fixing the path
Takes a bit more logic:
Strip /opt/bin out of the path.
Paste it onto the front.
Globs aren’t smart enough.
Fixing the path
Takes a bit more logic:
First break up the path.
> echo $PATH | tr ':' "n"
/opt/bin
/usr/local/bin
/usr/bin
/opt/bin
/bin
/usr/i486-pc-linux-gnu/gcc-bin/4.1.2
Fixing the path
Takes a bit more logic:
Then remove ‘/opt/bin’.
> echo $PATH | tr ':' "n" | grep -v '/opt/bin'
/usr/local/bin
/usr/bin
/bin
/usr/i486-pc-linux-gnu/gcc-bin/4.1.2
Fixing the path
Takes a bit more logic:
Recombine them.
> a=$(echo $PATH | tr ':' "n" |
grep -v '/opt/bin' | tr "n" ':');
> echo $a
/usr/local/bin:/usr/bin:/bin:/usr/i486-pc-linux-
gnu/gcc-bin/4.1.2::
Fixing the path
Takes a bit more logic:
Prefix ‘/opt/bin’.
> a=$(echo $PATH | tr ':' "n" |
grep -v '/opt/bin' | tr "n" ':');
> echo “/opt/bin:$a”;
/opt/bin:/usr/local/bin:/usr/bin:/bin:/usr/i486-
pc-linux-gnu/gcc-bin/4.1.2::
Fixing the path
Takes a bit more logic:
Or, as a one-liner:
> PATH=
"/opt/bin:$(echo $PATH | tr ':' "n" |
grep -v '/opt/bin' | tr -s "n" ':')";
> echo $PATH
/opt/bin:/usr/local/bin:/usr/bin:/bin:/usr/i486-
pc-linux-gnu/gcc-bin/4.1.2:
Quick version of basename
Strip off the longest match to ‘/’:
${file_path##*/}
Relative path within a home directory:
${file_path#$HOME}
Relative path in a sandbox directory:
${file_path##*/$(whoami)/}
Getting some tail
Clean up a directory: ${path%/}
Sandbox root: ${file%$(whoami)/*}
Root of home: ${HOME%$(whoami)*}
Less reliable dirname: ${file_path%/*}
Default values
Common use is with arguments.
> rm -rf $1/*;
What if $1 is empty?
> rm -rf /* # might not be what you want
Dealing with falsity
Common issue: Dealing with a NUL value.
Choose a default.
Assign a default.
Fail.
Use a default value
Lacking an argument, pick a value:
path=${1:-/var/tmp/input};
path=${1:-$input};
path=${1:-/var/cache/$(whoami)};
No effect on $1.
Assign a default value
Empty default assigned a value.
‘$’ interpolation may be nested:
“Default: ‘${default:=/var/tmp/$(whoami)}’”;
“:=” does not work with positional parameters ($1...).
Giving up
Maybe not providing a value is an error.
rm -rf ${path:?Path required.}/*
Code exits with “Path required.” prompt.
For example
#!/bin/bash
# if $1 has a value DEFAULT_PATH is ignored.
# empty $1 checks for non-empty default.
path=${1:-${DEFAULT_PATH:?Empty Default}};
# at this point path is not empty.
The next steps
Special Parameters:
$*, $@, $# Command line
$?, $$, $! Execution
Interpolate command line arguments, process control.
Summary
BASH interpolates variables in one pass.
${...} protect, slice variables
eval multi-pass processing.
<<TAG “here script”
-vx debugging
“Parameter Expansion” in bash(1)

More Related Content

PDF
Final thesis numerical simulation of gyroscopic effects in ansys 11.10
PDF
IBM III-V workshop 2010
PDF
Samba do avião
PDF
Relembrando o gonzagão
PDF
BSDM with BASH: Command Interpolation
PDF
One-Liners to Rule Them All
PPT
Talk Unix Shell Script
PPT
Talk Unix Shell Script 1
Final thesis numerical simulation of gyroscopic effects in ansys 11.10
IBM III-V workshop 2010
Samba do avião
Relembrando o gonzagão
BSDM with BASH: Command Interpolation
One-Liners to Rule Them All
Talk Unix Shell Script
Talk Unix Shell Script 1

Similar to BASH Variables Part 1: Basic Interpolation (20)

DOCX
Directories description
PPT
04 using and_configuring_bash
PPTX
Shell scripting
PDF
Unleash your inner console cowboy
PPTX
Licão 09 variables and arrays v2
PPSX
Learning Bash For linux Command Line
PDF
Shell scripting
ODP
Class 2
PDF
Bash production guide
PPT
ODP
ODP
PPTX
PDF
Shell intro
PDF
Unit 11 configuring the bash shell – shell script
PDF
Scripting and the shell in LINUX
PDF
Shell scripting
Directories description
04 using and_configuring_bash
Shell scripting
Unleash your inner console cowboy
Licão 09 variables and arrays v2
Learning Bash For linux Command Line
Shell scripting
Class 2
Bash production guide
Shell intro
Unit 11 configuring the bash shell – shell script
Scripting and the shell in LINUX
Shell scripting
Ad

More from Workhorse Computing (20)

PDF
Object::Trampoline: Follow the bouncing object.
PDF
Wheels we didn't re-invent: Perl's Utility Modules
PDF
mro-every.pdf
PDF
Paranormal statistics: Counting What Doesn't Add Up
PDF
The $path to knowledge: What little it take to unit-test Perl.
PDF
Unit Testing Lots of Perl
PDF
Generating & Querying Calendar Tables in Posgresql
PDF
Hypers and Gathers and Takes! Oh my!
PDF
Findbin libs
PDF
Memory Manglement in Raku
PDF
Effective Benchmarks
PDF
Metadata-driven Testing
PDF
The W-curve and its application.
PDF
Keeping objects healthy with Object::Exercise.
PDF
Perl6 Regexen: Reduce the line noise in your code.
PDF
Smoking docker
PDF
Getting Testy With Perl6
PDF
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
PDF
Neatly folding-a-tree
PDF
Light my-fuse
Object::Trampoline: Follow the bouncing object.
Wheels we didn't re-invent: Perl's Utility Modules
mro-every.pdf
Paranormal statistics: Counting What Doesn't Add Up
The $path to knowledge: What little it take to unit-test Perl.
Unit Testing Lots of Perl
Generating & Querying Calendar Tables in Posgresql
Hypers and Gathers and Takes! Oh my!
Findbin libs
Memory Manglement in Raku
Effective Benchmarks
Metadata-driven Testing
The W-curve and its application.
Keeping objects healthy with Object::Exercise.
Perl6 Regexen: Reduce the line noise in your code.
Smoking docker
Getting Testy With Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly folding-a-tree
Light my-fuse
Ad

Recently uploaded (20)

PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
Cloud computing and distributed systems.
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
NewMind AI Monthly Chronicles - July 2025
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
madgavkar20181017ppt McKinsey Presentation.pdf
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
GamePlan Trading System Review: Professional Trader's Honest Take
PDF
AI And Its Effect On The Evolving IT Sector In Australia - Elevate
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PDF
GDG Cloud Iasi [PUBLIC] Florian Blaga - Unveiling the Evolution of Cybersecur...
PPTX
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
PDF
Chapter 2 Digital Image Fundamentals.pdf
PPTX
Big Data Technologies - Introduction.pptx
PDF
cuic standard and advanced reporting.pdf
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
Electronic commerce courselecture one. Pdf
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Cloud computing and distributed systems.
“AI and Expert System Decision Support & Business Intelligence Systems”
NewMind AI Monthly Chronicles - July 2025
Understanding_Digital_Forensics_Presentation.pptx
madgavkar20181017ppt McKinsey Presentation.pdf
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
GamePlan Trading System Review: Professional Trader's Honest Take
AI And Its Effect On The Evolving IT Sector In Australia - Elevate
Advanced methodologies resolving dimensionality complications for autism neur...
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
GDG Cloud Iasi [PUBLIC] Florian Blaga - Unveiling the Evolution of Cybersecur...
Telecom Fraud Prevention Guide | Hyperlink InfoSystem
Chapter 2 Digital Image Fundamentals.pdf
Big Data Technologies - Introduction.pptx
cuic standard and advanced reporting.pdf
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Spectral efficient network and resource selection model in 5G networks
Electronic commerce courselecture one. Pdf

BASH Variables Part 1: Basic Interpolation

  • 1. Don’t BASH your head in: Rx for shell variables. Steven Lembark Workhorse Computing [email protected]
  • 2. Quick review BASH is interpreted. Loops are re-parsed. Variables can appear anywhere. Unlike Perl, Python, Ruby, Go, Haskell, Scala, Scheme, Which separate statements from var’s.
  • 3. Basic Variables Assignments to foo: foo=’$files’; literal ‘$files’.
  • 4. Basic Variables Assignments to foo: foo=’$files’; literal ‘$files’. foo=”$files”; interpolated value of files.
  • 5. Basic Variables Assignments to foo: foo=’$files’; literal ‘$files’. foo=”$files”; interpolated value of files. foo=$(ls $files); command output.
  • 6. Basic Variables Assignments to foo: foo=’$files’; literal ‘$files’. foo=”$files”; interpolated value of files. foo=$(ls $files); command output. foo=”$(ls $files)”; string with listing of files.
  • 7. Basic Variables Assignments to foo: foo=’$files’; literal ‘$files’. foo=”$files”; interpolated value of files. foo=$(ls $files); command output. Most of the work is interpolating: echo “Your files are: $(ls $somedir)”;
  • 8. De-mangling variable names > foo=’bar’; > echo “foo$foo”; ”foobar” > echo “$bar_foo”; “” Oops: Variable “bar_foo” doesn’t exist.
  • 9. De-mangling variable names > foo=’bar’; > echo “foo$foo”; ”foobar” > echo “$bar_foo”; “” Isolate ‘foo’ as variable name: “${foo}_bar # “bar_bar”
  • 12. Variable commands # interpolate each command into the loop for i in $cmd1 $cmd2 $cmd3 do $i $args; done
  • 15. Really, anywhere! foo=’bar’; $foo=’blort’; Q: What happens? A: Nada. bash: bar=blort: command not found
  • 16. Your one chance at success BASH parses in two phases: Lexical substitution & tokenizing. Execution. Variables have to expand on the first pass to be used. “foo=blort” cannot be executed, so it failed.
  • 17. A second chance in life ‘eval’ adds one cycle. Interpolates variables. Passes result to the shell. ‘++’ is two levels deep. > eval"$foo=blort"; + eval bar=blort ++ bar=blort > echo $bar; + echo blort blort
  • 18. Or a third chance... eval “eval … “; Work out what is happening: a=’$HOME/?.*’; b=’foo’; c=eval “eval $a $b”’;
  • 19. Verbosity & Execution See what bash is doing with the variables: > set -vx;
  • 20. Verbosity & Execution See what bash is doing with the variables: > set -vx; echo -ne "033]0;./$(basename $PWD) 007" +++ basename /sandbox/lembark/writings/RockfordLUG/bash ++ echo -ne '033]0;./bash 007' >
  • 21. Verbosity & Execution See what bash is doing with the variables: > set -vx; echo -ne "033]0;./$(basename $PWD) 007" +++ basename /sandbox/lembark/writings/RockfordLUG/bash ++ echo -ne '033]0;./bash 007' >
  • 22. “unset” removes variables > unset PROMPT_COMMAND; > set -vx; >
  • 23. Or set verbosity in the script #!/bin/bash -vx … or localize the verbosity: set -vx; do something set -;
  • 24. Verbosity & Execution > unset PROMPT_COMMAND; > set -vx; > foo=bar; what I typed foo=bar; what BASH read + foo=bar single ‘+’is one level deep
  • 25. Verbosity & Execution > unset PROMPT_COMMAND; > set -vx; > foo=bar; what I typed foo=bar; what BASH read + foo=bar single ‘+’is one level deep > $foo='blort'; >foo='blort'; + bar=blort no second chance to re-parse
  • 26. Verbosity & Execution > unset PROMPT_COMMAND; > set -vx; > foo=bar; what I typed foo=bar; what BASH read + foo=bar single ‘+’is one level deep > $foo='blort'; >foo='blort'; + bar=blort no second chance to re-parse bash: bar=blort: command not found
  • 27. Why use ‘-vx’? -v alone is useful sometimes. -x alone usually isn’t.
  • 28. Example: Hidden disk hogs #!/bin/bash dirs="/home/$(whoami)/.*"; cmd="du -msx $dirs | sort -rn | head -3"; eval "$cmd";
  • 29. Buggy hogs $ ./y 1232 /home/lembark/. 0 /home/lembark/..
  • 30. Add verbosity #!/bin/bash set -vx; dirs="/home/$(whoami)/.*"; cmd="du -msx $dirs | sort -rn | head -3"; eval "$cmd";
  • 31. Shows the bug: . & .. $ ./y dirs="/home/$(whoami)/.*"; ++ whoami + dirs='/home/lembark/.*' cmd="du -msx $dirs | sort -rn | head -3"; + cmd='du -msx /home/lembark/.* | sort -rn | head -3' eval "$cmd"; + eval 'du -msx /home/lembark/.* | sort -rn | head -3' du -msx /home/lembark/.* | sort -rn | head -3 ++ sort -rn ++ head -3 ++ du -msx /home/lembark/. /home/lembark/.. /home/lembark/.aceinnovative /home/lembark/.adobe <snip> 1232 /home/lembark/. 0 /home/lembark/..
  • 32. Fix: Add a letter #!/bin/bash set -vx; dirs="/home/$(whoami)/.[a-z]*"; cmd="du -msx $dirs | sort -rn | head -3"; eval "$cmd";
  • 33. Start with the right dirs $ ./y dirs="/home/$(whoami)/.[a-z]*"; ++ whoami + dirs='/home/lembark/.[a-z]*' cmd="du -msx $dirs | sort -rn | head -3"; + cmd='du -msx /home/lembark/.[a-z]* | sort -rn | head -3' eval "$cmd"; + eval 'du -msx /home/lembark/.[a-z]* | sort -rn | head -3' du -msx /home/lembark/.[a-z]* | sort -rn | head -3 ++ sort -rn ++ head -3 ++ du -msx /home/lembark/.aceinnovative /home/lembark/.adobe <snip> 270 /home/lembark/.openoffice 244 /home/lembark/.mozilla 193 /home/lembark/.config
  • 34. Compare: Only “-v” $ ./y dirs="/home/$(whoami)/.[a-z]*"; cmd="du -msx $dirs | sort -rn | head -3"; eval "$cmd"; du -msx /home/lembark/.[a-z]* | sort -rn | head -3 270 /home/lembark/.openoffice 244 /home/lembark/.mozilla 193 /home/lembark/.config
  • 35. Compare: Only “-x” $ ./y ++ whoami + dirs='/home/lembark/.[a-z]*' + cmd='du -msx /home/lembark/.[a-z]* | sort -rn | head -3' + eval 'du -msx /home/lembark/.[a-z]* | sort -rn | head -3' ++ sort -rn ++ head -3 ++ du -msx /home/lembark/.aceinnovative /home/lembark/.adobe <snip> 270 /home/lembark/.openoffice 244 /home/lembark/.mozilla 193 /home/lembark/.config
  • 36. Command execution We all remember backticks: a=`ls -al ~`’;
  • 37. Command execution We all remember backticks: a=`ls -al ~`’; Better off forgotten: No way to nest them for one. Hard to read for another.
  • 38. Command execution BASH offers a better way: $( ... ) i.e., “interpolate subshell output”. Output of arbitrary commands: files=$(ls ~); jobs=$( grep ‘MHz’ /proc/cpuinfo | wc -l ); echo -e “DiskHogz:n$(du -msx *|sort -rn|head)”;
  • 39. Twisting a path with basename basename locates input for next step. cmd=’/image/bin/extract-hi-res’; dir=’../raw’; cd high-res || exit -1; for i in ../low-res/culled/*; do echo “Input: ‘$i’”; $cmd $dir/$(basename $i .ppm).nef; done
  • 40. Twisting a path with basename Quotes hilite whitespace in $1. Don’t leave home without them... cmd=’/image/bin/extract-hi-res’; dir=’../raw’; cd high-res || exit -1; for i in ../low-res/culled/*; do echo “Input: ‘$i’”; $cmd $dir/$(basename $i .ppm).nef; done
  • 41. Being there A “here script” is “appended from stdin”. Double-quotish. > perl -MCPAN -E shell <<CPAN 2>&1 | tee a; upgrade install Module::FromPerlVer q CPAN
  • 42. Being there A “here script” is “appended from stdin”. Double-quotish, into stdin. > perl -MCPAN -E shell <<CPAN 2>&1 | tee a; upgrade install Module::FromPerlVer q CPAN
  • 43. Being there Closing tag sends EOF (^D) to command: > perl -MCPAN -E shell <<CPAN 2>&1 | tee a; upgrade install Module::FromPerlVer CPAN
  • 44. Being there module=’Module::FromPerlVer’; > perl -MCPAN -E shell <<CPAN 2>&1 | tee a; upgrade install $module CPAN
  • 45. Being there #!/bin/bash ... path=”$mysql_d/$tspace”; mkdir -p $path || exit -2; mysql -U$user -P$pass <<SQL || exit -3; create tablespace $tspace using ‘$path’ … ; create table big ( … ) tablespace $tspace; SQL
  • 46. Being there mysql -U$user -P$pass <<SQL || exit -3; create tablespace $tspace using ‘$path’ … ; create table $(cat $table-1.sql) tablespace $tspace; SQL
  • 47. Slicing with curlies Remove strings from the head or tail of a string. ${i#glob} ${i%glob} ${i##glob} ${i%%glob}
  • 48. Slicing with curlies Slice the head: ${i#glob} ${i##glob} # is shortest match ## is longest match
  • 49. Slicing with curlies Slice the tail: ${i%glob} ${i%%glob} % is shortest match %% is longest match
  • 50. Stripping a prefix. Say you want to prefix ‘/opt/bin’ onto a PATH. But it may already be there. You don’t know if someone else hacked the path. Q: How can we put ‘/opt/bin’ at the front, once?
  • 51. Stripping a prefix. Say you want to prefix ‘/opt/bin’ onto a PATH. But it may already be there. You don’t know if someone else hacked the path. Q: How can we put ‘/opt/bin’ at the front, once? A: Take it off each time.
  • 52. Striptease. ‘#’ strips off leading content. Say we tried this: PATH=”/opt/bin:${PATH#/opt/bin:}”; OK, I can run it a hundred times.
  • 53. Path hack striptease. ‘#’ strips off leading content. Say we tried this: PATH=”/opt/bin:${PATH#/opt/bin:}”; OK, I can run it a hundred times. Until /opt/bin isn’t first: “~/bin:/opt/bin: ...”
  • 54. Globs save the day Find everything up to the first match: PATH=”/opt/bin:${PATH#*/opt/bin:}”; > echo $PATH; /usr/local/bin:/usr/bin:/bin:/opt/bin:/usr/ i486-pc-linux-gnu/gcc-bin/4.1.2
  • 55. Globs save the day Find everything up to the first match: PATH=”/opt/bin:${PATH#*/opt/bin:}”; > echo ${PATH#*/opt/bin:}; + echo /usr/local/bin:/usr/bin:/bin:/opt/bin:/ usr/i486-pc-linux-gnu/gcc-bin/4.1.2
  • 56. Globs save the day Find everything up to the first match: PATH=”/opt/bin:${PATH#*/opt/bin:}”; > echo ${PATH#*/opt/bin:}; + echo /usr/local/bin:/usr/bin:/bin:/opt/bin:/ usr/i486-pc-linux-gnu/gcc-bin/4.1.2
  • 57. Globs save the day Find everything up to the first match: PATH=”/opt/bin:${PATH#*/opt/bin:}”; /usr/i486-pc-linux-gnu/gcc-bin/4.1.2
  • 58. Fixing the path Takes a bit more logic: Strip /opt/bin out of the path. Paste it onto the front. Globs aren’t smart enough.
  • 59. Fixing the path Takes a bit more logic: First break up the path. > echo $PATH | tr ':' "n" /opt/bin /usr/local/bin /usr/bin /opt/bin /bin /usr/i486-pc-linux-gnu/gcc-bin/4.1.2
  • 60. Fixing the path Takes a bit more logic: Then remove ‘/opt/bin’. > echo $PATH | tr ':' "n" | grep -v '/opt/bin' /usr/local/bin /usr/bin /bin /usr/i486-pc-linux-gnu/gcc-bin/4.1.2
  • 61. Fixing the path Takes a bit more logic: Recombine them. > a=$(echo $PATH | tr ':' "n" | grep -v '/opt/bin' | tr "n" ':'); > echo $a /usr/local/bin:/usr/bin:/bin:/usr/i486-pc-linux- gnu/gcc-bin/4.1.2::
  • 62. Fixing the path Takes a bit more logic: Prefix ‘/opt/bin’. > a=$(echo $PATH | tr ':' "n" | grep -v '/opt/bin' | tr "n" ':'); > echo “/opt/bin:$a”; /opt/bin:/usr/local/bin:/usr/bin:/bin:/usr/i486- pc-linux-gnu/gcc-bin/4.1.2::
  • 63. Fixing the path Takes a bit more logic: Or, as a one-liner: > PATH= "/opt/bin:$(echo $PATH | tr ':' "n" | grep -v '/opt/bin' | tr -s "n" ':')"; > echo $PATH /opt/bin:/usr/local/bin:/usr/bin:/bin:/usr/i486- pc-linux-gnu/gcc-bin/4.1.2:
  • 64. Quick version of basename Strip off the longest match to ‘/’: ${file_path##*/} Relative path within a home directory: ${file_path#$HOME} Relative path in a sandbox directory: ${file_path##*/$(whoami)/}
  • 65. Getting some tail Clean up a directory: ${path%/} Sandbox root: ${file%$(whoami)/*} Root of home: ${HOME%$(whoami)*} Less reliable dirname: ${file_path%/*}
  • 66. Default values Common use is with arguments. > rm -rf $1/*; What if $1 is empty? > rm -rf /* # might not be what you want
  • 67. Dealing with falsity Common issue: Dealing with a NUL value. Choose a default. Assign a default. Fail.
  • 68. Use a default value Lacking an argument, pick a value: path=${1:-/var/tmp/input}; path=${1:-$input}; path=${1:-/var/cache/$(whoami)}; No effect on $1.
  • 69. Assign a default value Empty default assigned a value. ‘$’ interpolation may be nested: “Default: ‘${default:=/var/tmp/$(whoami)}’”; “:=” does not work with positional parameters ($1...).
  • 70. Giving up Maybe not providing a value is an error. rm -rf ${path:?Path required.}/* Code exits with “Path required.” prompt.
  • 71. For example #!/bin/bash # if $1 has a value DEFAULT_PATH is ignored. # empty $1 checks for non-empty default. path=${1:-${DEFAULT_PATH:?Empty Default}}; # at this point path is not empty.
  • 72. The next steps Special Parameters: $*, $@, $# Command line $?, $$, $! Execution Interpolate command line arguments, process control.
  • 73. Summary BASH interpolates variables in one pass. ${...} protect, slice variables eval multi-pass processing. <<TAG “here script” -vx debugging “Parameter Expansion” in bash(1)