Bash Notes BASH Home Page (at Case Western Reserve University)

11-15-2010 comment to this article mentions OO bash!?

nice tutorial w/ concise examples!

from a commenter: A metric ton of usefull shortcuts and hints for the Bash Shell

1-20-2011 from 2000

An A-Z Index of the Bash command line for Linux.

| | | | |

bash variables and references to variables...
a=1; b=`expr $a + 1`; echo $a $b note that assignments must NOT have spaces around "=" sign,
but `expr arg op arg` Must have spaces around op - "+" in this case. BTW, expr is not part of bash; just another "unix" tool
variables are defined as alpha tokens like a, found_file, debug_flag, rc,
but need "$" prefixed to variable name to get their contents...
also, can put multiple bash commands on a line, separated by ";"
bash variables are treated as strings, so $b=$a+1 results in "1+1", not 2
numeric calculations also possible within bash: let b=a+1 -or- ((b=a+1)) Note: don't need leading "$" !

How can we tell that the ppp connection is up so we can proceed to execute cmd to xfr, compact the data...

from TRAMS ($i$cb gets resolved to filename in /etc/ppp/peers containing options and chat -v chatscript-filename)
($i is also in the /etc/hosts table, so the network that appears in the routing table is identified with the same $i string...)

for i in PHL JFK LAXN; do
#call ppp cannection
  /usr/sbin/pppd call $i$cb &
  # give the connection time to get established
  echo "started pppd connection"
  sleep 90
  # every 10 seconds ping the remote computer. Exit the loop when it returns.
  while [ "$tmp" == "" ]
	sleep 10
        tmp=`/sbin/route | grep $i`
  echo "connection up!"
#  pppd_pid=`cat /var/run/`
  ppp_dev=`/sbin/route | grep $i | cut -c "73-"`
  pppd_pid=`cat /var/run/$`
(pppd_pid now contains the process-id of this pppd session; there may be simultaneous modem conversations on seperate serial lines, seperate ppp sessions - so when this transaction is finished, the script can hangup the modem line on this particular pppd session)

from FileComp from Adv Bash Scripting (ABS), Linux Doc Project:
$# is count of arguments
here's a script that tests it:


ARGS=2  # Two args to script expected.

if [ $# -ne "$ARGS" ]
  echo "Usage: `basename $0` file1 file2"
  exit $E_BADARGS

if [[ ! -r "$1" || ! -r "$2" ]]
  echo "Both files to be compared must exist and be readable."

cmp $1 $2 &> /dev/null  # /dev/null buries the output of the "cmp" command.
#   cmp -s $1 $2  has same result ("-s" silent flag to "cmp")
#   Thank you  Anders Gustavsson for pointing this out.
# Also works with 'diff', i.e.,   diff $1 $2 &> /dev/null

if [ $? -eq 0 ]         # Test exit status of "cmp" command.
  echo "File \"$1\" is identical to file \"$2\"."
  echo "File \"$1\" differs from file \"$2\"."

exit 0

from exit-status from Adv Bash Scripting (ABS), Linux Doc Project:

#  By convention, an 'exit 0' indicates success,
#+ while a non-zero exit value means an error or anomalous condition.


echo hello
echo $?    # Exit status 0 returned because command executed successfully.

Application: bash script to detect if an IP address is up
Actually, it just returns a 0 is successful, 1 if not -

I called the file

ping -c 1 $1
echo $?

to use it, chmod +x
(that's me)
should return 0

bogus ip address returns 1

If the PPP connection is working, this script will determine whehter you can ping the remote end ( I think)

top Cliches

if [ -z "$1" ]; then echo "fail"; exit 0; fi (if there is no first arg, fail)
[ -z "$1" ] && echo "fail" && exit 0 (alt 11-29-2010 - if there is no first arg, fail)

cp /foo /bar && ( echo Success ; echo Success part II ) || ( echo Failed ; echo Failed part II )

This code will either echo

Success part II
  OR   Failed
Failed part II
depending on whether or not the cp command was succesful.

overwrite file : > your_file - a quick way to delete all contents of a file...

googled bash string include form feed

googled bash string manipulation split

top One-Line Gems

one-line gems from 9-2-2009:
ls | while read f; do mv "$f" "${f// /_}";done Replace spaces in filenames with underscores
an alternative: rename -v 's/ /_/g' *
ls *.c | while read F; do gcc -Wall -o `echo $F | cut -d . -f 1 - ` $F; done Compile all C files in a directory
an alternative: for F in *.c; do gcc -Wall -o ${F%.c} $F; done

9-9-2009: remove one line from a file, in particular, HWADDR from /etc/sysconfig/network-scripts/ifcfg-eth0
gawk '!/HWADDR/ {print} ifcfg-eth0 > my_ifcfg-eth0
perl -ne 'print unless /HWADDR/' ifcfg-eth0 > my_ifcfg-eth0
perl -nie 'print unless /HWADDR/' ifcfg-eth0
sed -i '/^HWADDR/d' ifcfg-eth0

cat </dev/tcp/
Reads the time in Daytime Protocol (RFC-867) from the NIST Internet Time Service server (documented at

Shell Programming, One-Liners, Tips and Tricks

Shell scripts in 20 pages

Sed - An Introduction and Tutorial, by Bruce Barnett; Last update: Sat Apr 17 13:33:25 EDT 2010

top bashrc

define, export environmental variables in .bashrc
export INFO_PRINT_COMMAND='enscript -G' in gnu-info, M-x print-node uses enscript to print the text of a node instead of raw lpr
export PS1='[\u@\h \e[0;34m\d \@\e[m \W]\$ ' sets prompt to [user@host <blue>Day-of-Week Month Day HH:MM AM/PM</blue> pwd ]$
export PATH=$PATH:$HOME/bin:$HOME/tecoLinux/tecoc0398/bin adds directory to path

Colors in bash:

Black       0;30     Dark Gray     1;30
Blue        0;34     Light Blue    1;34
Green       0;32     Light Green   1;32
Cyan        0;36     Light Cyan    1;36
Red         0;31     Light Red     1;31
Purple      0;35     Light Purple  1;35
Brown       0;33     Yellow        1;33
Light Gray  0;37     White         1;37

says ISO 6429 (ANSI) COLOR SEQUENCES are: (for ls, PS1)

 0     to restore default color
 1     for brighter colors 
 4     for underlined text
 5     for flashing text
30     for black foreground
31     for red foreground
32     for green foreground
33     for yellow (or brown) foreground
34     for blue foreground
35     for purple foreground
36     for cyan foreground
37     for white (or gray) foreground
40     for black background
41     for red background
42     for green background
43     for yellow (or brown) background
44     for blue background
45     for purple background
46     for cyan background
47     for white (or gray) background

also focusing on the prompt:

can one set aliases to change the bkgrnd color of a gnome-terminal?
says try man gnome-terminal (nothing), gconf-editor (nothing) gconftool (nothing)... maybe in Ubuntu?

top Pipe to the whatis command

per whatis is a shortcut to the apropos or man -k utilities.
for example you can enter whatis tcpdump and get the some of the same results as apropos tcpdump or man -k tcpdump

to find out what each of a list of programs might do, say in /etc/rc.d/init.d - a directory involved in system startup,

 ls | xargs whatis | less

for me, the new command here is "xargs"
xargs = build and execute command lines from standard input

the article suggests doing this when in these directories:


apropos of nothing, article 12 also mentions cooledit (illustrating apropos cool)
a full-featured text editor for the X Window System. ver 3.17.17 is latest.

3-4-2009: discovered REALLY cool use of xargs:

compgen -c ss | xargs whatis
compgen -c does the same thing as [tab][tab] on the bash command-line! - xargs hands each completion to whatis for a 1-liner...

compgen generates lots of other cool completions too!

top Command line useradd

per add a user for the cvs system (?) via

useradd -M -c "CVS User" -u 150 -s /sbin/nologin cvs
man useradd says
-c is Comment - any text string
-u is the UID (user ID)
-s is the shell
-M the user's home directory WILL NOT be created, even if the system wide settings from /etc/login.defs is to create home dirs.

useradd -p secret george
will add user george with /home/george, login shell /bin/bash, new UID and GID - password secret
Actually, trials suggest the -p secret Doesn't set the password right... NOTE: see linux_users.html useradd
elemenet k's Linux System Administration: LPI Certification recipe (p. 167) is
useradd george; password george then type secret, secret

related commands: groudadd, grouddel, userdel usermod, chfn, chsh, crypt, newusers

newusers new
where file new has

batch adds 3 users. Note that the passwords are cleartext! so this file should be protected...

top Arithmetic

z=`expr $z + 3`
z=$((z+3))                                  #  Also correct.
                                            #  Within double parentheses,
                                            #+ parameter dereferencing
                                            #+ is optional.

(( n += 1 ))                              # Increment.

let z=z+3
let "z += 3"  #  Quotes permit the use of spaces in variable assignment.
              #  The 'let' operator actually performs arithmetic evaluation,
              #+ rather than expansion.

Conditional Assignment (thanks
Many times we want a conditionally assign a value to a variable VVV. The syntax VVV=${ZZZ:-DefaultVal} is equivalent to

     # same as 
if [ "$ZZZ" != "" ]; then
Thus we assign the value of $ZZZ to VVV if ZZZ has a value, otherwise we assign DefaultVal.

another fine stunt:

top Strings

see bash scripting: string-manipulation

echo "The string being operated upon is \"$a\"."

# length: length of string
b=`expr length $a`
echo "Length of \"$a\" is $b."

# index: position of first character in substring
#        that matches a character in string
b=`expr index $a 23`
echo "Numerical position of first \"2\" in \"$a\" is \"$b\"."

# substr: extract substring, starting position & length specified
b=`expr substr $a 2 6`
echo "Substring of \"$a\", starting at position 2,\
and 6 chars long is \"$b\"."

Convert the given argument into an all lower case string.

toLower() {
  echo $1 | tr "[:upper:]" "[:lower:]" 

Convert the given argument into an all lower case string.

toUpper() {
  echo $1 | tr "[:lower:]" "[:upper:]" 

String concatenation: (per
j="foo" to tack on "bar" -

echo {a,b,c}{1,2,3} produces a1 a2 a3 b1 b2 b3 c1 c2 c3 !

Using set with the -- option explicitly assigns the contents of a variable to the positional parameters.

variable="one two three four five"
set -- $variable
$1 is now "one", etc.

how to find substring position in a given string


split IP=; IP=(${IP//./ }); Rev=${IP[3]}.${IP[2]}.${IP[1]}.${IP[0]}

another example (leaves IFS set to ".")
# IFS="."
# s=21.14.51
# set -- $s
# arr=( $s )
# echo ${arr[0]} ${arr[1]} ${arr[2]}
21 14 51

another example (uses Associative Array)
# Split the command line argument on the colon character.

declare -a Array=($*)
IFS=SaveIFS               # probably should be IFS=$SaveIFS

echo "Array[0]=${Array[0]}"
echo "Array[1]=${Array[1]}"
echo "Array[2]=${Array[2]}"
echo "Array[3]=${Array[3]}"

11-16-2010 from about IFS:

bash -c 'set w x y z; IFS=":"; echo "$*"'
(! you can invoke bash with -c providing pgm inside ' '!)
(! echo "$*" writes out arguments; set w x y z places these values? in args!)
see what IFS is currently: $ echo "$IFS" | cat -e - the "-e flag" means print $ indicating eol

unset IFS returns IFS to default value " "

top trim strings

in, I set IFS to ",", read successive lines into a string

exec 5<$input_filename; IFS=","
sometimes get a leading space trapped into a variable!
bash usually trims leading and trailing spaces? depends on "IFS"
g=" h"
echo $g 
g=" h"
echo $g 
bash pattern helps remove space:
echo ${g// /}
but, it removes ALL spaces in $g, not just leading one
maybe a job for sed!
found a solution (uses sed) at
var=" h i"; var=$(sed -e 's/^[[:space:]]*//' <<<"$var"); echo $var => h i (no leading space)
also works: var=$(sed -e 's/^ *//' <<<"$var");

top dd

Googled linux dd zero disk sector
found How_to_zero_a_hard_drive_using_Linux_dd:  dd if=/dev/zero of=/dev/TARGETDRIVE bs=1M

and learn-the-dd-command
LOTS of great stuff!


Back-up, restore MBR per linuxgazette #159, Using The Red Hat Rescue Environment

dd if=/dev/sda of=mbr-parttable bs=512 count=1
dd if=mbr-parttable of=/dev/sda bs=512 count=1

top install script

This install bash script features a couple of Really Clever techniques for installing J (computer language) in linux
following this snippet is the binary zipped tar file (not included here)!


fail(){ echo -e "\nInstallation failed.\n" ; exit 1 ; }

Run with parameter: -install\n\
 for a standard install of $product in your home folder.\n\n\
Run with parameters: -install your_path\n\
 to install $product in the folder of your choice.\n"

if [ $# -eq 0 ] ; then
echo -e $info
exit 0

if [ $1 != "-install" ]; then
echo -e $info
exit 0

if [ $# -gt 2 ] ; then
echo -e "Too many parameters.\n"
exit 0

if [ $# -eq 1 ] ; then

mkdir -p "$folder" || fail
tail -n +$lines $0 >"$folder/install.tar.gz" || fail
cd "$folder" || fail
tar -xzf install.tar.gz || fail
rm install.tar.gz

echo Run Jwd with command: \"$folder/$product/bin/jwd\"
echo Run Jconsole with command: \"$folder/$product/bin/jconsole\"
echo jwd problems and info: \"$folder/$product/help.htm\"

exit 0

top arrays

googled bash test is in array, found (Bash Guide For Beginners):

if [ -z $1 ]; then
echo "Usage: $0 "
exit 1


farm_hosts=(web03 web04 web05 web06 web07)

for i in ${farm_hosts[@]}; do
su $login -c "scp $httpd_conf_new ${i}:${httpd_conf_path}"
su $login -c "ssh $i sudo /usr/local/apache/bin/apachectl graceful"

array tests from

an example from that shows length of array:

NAMESERVERS=("" "" "")
# get length of an array
# use for loop read all nameservers
for (( i=0; i<${tLen}; i++ ));
  echo ${NAMESERVERS[$i]}

another exercise: (from Bash: Check If Element Exists in Array

cd /root/s3sync/


excluded=( /backups /dev /lost+found /media /mnt /proc /sys /tmp )

for dir in $dirs
  ruby s3sync.rb -rs --delete $dir azavia:backups/daily
function elementExists()

if [ -z "$1" ]

for i in ${excluded[@]}
if [ $i == $1 ]
return 1

return 0

excellent Linux Journal article: bash-arrays (June 19th, 2008 by Mitch Frazier in * HOWTOs)


# myarray=( 4 5 4 16 8 34 7 457 25 )
# count=0
# for i in ${myarray[@]}; do myarray[$count]=`expr $i + 2`; ((count++));done
# echo ${myarray[@]}
6 7 6 18 10 36 9 459 27

This works! per as tho arg=$@; any dummy array name (e.g. sites) works

for arg; do
  echo "$arg"

loop on array index per



echo ${!array[*]} # find all the indexes

# print all indexes and associated values
for I in ${!array[*]}; do
  echo $I: ${array[$I]}
5: tom
13: jenny
77: julie

Another array ex from
myarray=($(df -h))

operations on arrays: can change single entries

a=(pHl jfK laxN)
echo ${b[@]}
(phl jfK laxN)

Bash split functionality: (googled bash split) 21:14:51 -> [21][14][51}

echo "$string1" | read arr1[1] arr1[2] arr1[3]
echo "$string2" | read arr2[1] arr2[2] arr2[3]
IFS=" "
arr1=( `echo "$string1" | tr -s ':' ' '` )
a="the quick brown fox"
ar=( `echo "$a"` )
echo ${ar[@]}   #  -> the quick brown fox
$ echo $ar      #  -> the
$ b=`echo ${ar[@]}`
$ echo $b       #  -> the quick brown fox
expr index "$b" "row"  #  -> 12
expr index "$b" r # -> 12
expr index "$b" "frog" #  -> 12  !
expr index "$b" giraffe # -> 3  !!

bash seq-like feature:

echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

array and arg-list exercises

$ a=(q w e r t y)
$ echo $a
$ echo ${a[@]}
q w e r t y
$ echo ${#a[@]}
$ for i in ${a[@]}; do echo -n $i\ ; done; echo;
q w e r t y
$ for i in ${!a[@]}; do echo -n $i\ ; done; echo;
0 1 2 3 4 5
$ echo ${a[4]}
$ a () { for i in $@; do echo -n $i\ ; done; echo; }
$ a r t y
r t y
$ b () { for i; do echo -n $i\ ; done; echo; }
$ b r t y
r t y
$ c () { for i; do echo -n $1\ ; shift; done; echo; }
$ c r t y
r t y

11-16-2010 has A nice code example: Checking for duplicate files using an associative array indexed with the SHA sum of the files:

# Thanks to Tramp in #bash for the idea and the code

unset flist; declare -A flist;
while read -r sum fname; do 
    if [[ ${flist[$sum]} ]]; then
        printf 'rm -- "%s" # Same as >%s<\n' "$fname" "${flist[$sum]}" 
done <  <(find . -type f -exec sha256sum {} +)  >rmdups

top cut

per Text Processing in Adv Bash Scripting, Mendel Cooper:

cut -d ' ' -f2,3 filename is equivalent to awk -F'[ ]' '{ print $2, $3 }' filename

Using cut to list the OS and kernel version: uname -a | cut -d" " -f1,3,11,12

another important use of cut: extract data per exact column numbers:
in a tcl script,
set ipaddr [exec grep IPADDR /etc/sysconfig/network-scripts/ifcfg-eth0 | cut -c8-22 | sed "s/ //g"]
in ~tcl; ls -ltrs | grep rcomb | cut -c 50-66 -> rcomb.tcl
in ~tcl; ls -ltrs | grep rcomb | cut -c 50- -> rcomb.tcl

top loops

googled bash while read, found loops in bash

Re: different behaviour of 'while read' and 'for' loops in bash
On Fri, 13 May 2005 at 13:17 GMT, Glenn Morris wrote:
> I have a command I want to execute on several hosts via ssh. The hosts
> are listed in a file "hosts.txt", one per line. I'm using bash.
> First I tried this:
> while read host; do
> ssh $host hostname
> done < hosts.txt
> but for some reason this only does the first host listed in hosts.txt,
> not the rest. The uglier:
> for host in `cat hosts.txt`; do
> ssh $host hostname
> done
> works fine and processes all hosts as it should.
> All I can think is that the ssh in the first form somehow causes bash
> to lose its place in the file "hosts.txt", but I have no idea how to
> check that, or if that is to be expected...?

The while loop is reading from stdin; so is ssh. Use exec to
redirect the input:

exec 3< hosts.txt
while read -u3 host
ssh $host hostname
exec 3<&-

function p2() { echo $(($1+2)); }
for i in 5 6 7; do p2 $i; done

$ for i in {1..10}; do echo -n "$i "; done
1 2 3 4 5 6 7 8 9 10
$ for i in {3..23..2}; do echo -n "$i "; done
3 5 7 9 11 13 15 17 19 21

top File I/O

cites Mastering Unix Shell Scripting: Bash, Bourne, and Korn Shell Scripting for Programmers, System Administrators, and UNIX Gurus [Paperback], @2008, Randal K. Michael

Reading a file line by line. The book by Randal Michael contains 12 example ways to read a file line by line, which vary tremendously in efficiency. This example shows the simplest and fastest way.
most straight-forward first: while read line - from the bottom
 if [ -z "$1" ]; then
    echo "Usage: $0 filename"
    if [ -r $1 ]; then
       while read line; do echo "$line"; done < $1
another equally fast way uses File Directives:
 if [ -z "$1" ]; then
    echo "Usage: $0 filename"
    if [ -r $1 ]; then
       exec 3<&0
       exec 0<$1
       while read line; do echo "$line"; done
       exec 0<&3
using functions:
  exec 3<&0
  exec 0<$1
  while read line; do echo "$line"; done
  exec 0<&3

 if [ -z "$1" ]; then
    echo "Usage: $0 filename"
    if [ -r $1 ]; then while_read_FD $1; fi

top functions


function swap()  # Swap 2 filenames around, if they exist
{                #(from Uzi's bashrc).
    local TMPFILE=tmp.$$ 

    [ $# -ne 2 ] && echo "swap: 2 arguments needed" && return 1
    [ ! -e $1 ] && echo "swap: $1 does not exist" && return 1
    [ ! -e $2 ] && echo "swap: $2 does not exist" && return 1

    mv "$1" $TMPFILE 
    mv "$2" "$1"
    mv $TMPFILE "$2"

function extract()      # Handy Extract Program.
     if [ -f $1 ] ; then
         case $1 in
             *.tar.bz2)   tar xvjf $1     ;;
             *.tar.gz)    tar xvzf $1     ;;
             *.bz2)       bunzip2 $1      ;;
             *.rar)       unrar x $1      ;;
             *.gz)        gunzip $1       ;;
             *.tar)       tar xvf $1      ;;
             *.tbz2)      tar xvjf $1     ;;
             *.tgz)       tar xvzf $1     ;;
             *.zip)       unzip $1        ;;
             *.Z)         uncompress $1   ;;
             *.7z)        7z x $1         ;;
             *)           echo "'$1' cannot be extracted via >extract<" ;;
         echo "'$1' is not a valid file"

from LinuxGazette #53 create a function from the command line:

$ gla() {
> ls -la | grep $1 | awk ' { if ( $5 > 1024 ) print $0 } '
> }
You can't do this as an alias, you're no longer just replacing gla with the 'ls -la | grep'. Since its written as a function, there is no problem using the $1 (referring to the first argument to gla) anywhere in the body of your commands.

can store bash functions in a file, "source" them into other scripts:

# a sample script
#   first source the functions
. ~/scripts/setcvs

top mkfifo

from man script (!)
mkfifo foo; script -f foo in one window
in another window (same directory), cat foo follows along char by char what happens in first window!

BTW, script aa starts a sub-shell amd captures all keyboard entries and responses in file aa (which sometimes surprises: backspaces, and other escaped keyboard entries get saved, not just the final text on the screen before hitting enter)
first thing you might want to do is PS1='\$ ' to get rid of esc seq in prompt refreshes after every command!
exit script with ^d

top mk_environment

Envision a script which does initial environment setup (.bashrc, .emacs, printers) for TCAS lab environment.

command-line define printer
/usr/sbin/lpadmin -p HP4600 -E -v socket://
can also specify model (ppd) with -m flag
/etc/cups/ppd on Fedora 8 has one entry: JetDirect.ppd - if you specify -m JetDirect.ppd, you get "lpadmin: Unable to copy ppd file!"
see possible (foomatic:) ppds by lpinfo -m | grep 4600

see printers installed in "/etc/cups/printers.conf"

add "-d HP4600" to end of lpadmin command to make it the default printer

lpstat -s reports
system default destination: HP4600
device for HP4600: socket://

top Links