Shells & Shell Programming

Linux User's Guide

Shells & Shell Programming

Version 1.2 19/8/2003 by Samar

  1. Shells & Comparison: tcsh, zsh, ash, etc.
  2. bash
  3. CygWin
  4. Shell Programming (bash)


I. Shells

Definition of Shell : - Program layer providing a human interface point and user environment for communication with the Linux kernel

Different shells exist in Linux :


II. bash (Bourne Again Shell)

a. Features of bash

There are also useful tests for shell programming:

b. Shell Initialization and De-Initialization

  1. /etc/profile is with /etc/bashrc the default initialization file for bash.
  2. ~/.bash_profile
    ~/.bash_login
    ~/.profile   (bash searches for the first of these files and executes them; ~/.bashrc is often made to point to any of these so as to permit non-interactive login initialization)
  3. ~/.bash_logout   (executed on logout)
  4. ~/.bashrc   (subshell or non-login shell initialization file)

As an example, insert the line cat face.txt (where face.txt contains an illustrated face) into ~/.bash_history

c. Shell Environment Variables

d. Shell Meta-Characters

The following are shell meta-characters, also known as wildcards: The following are important shell symbols:

In order to prevent the > operator from overwriting the file, use $noclobber=1 (or anything) ($set noclobber for tcsh), or simply use >> .


x. CygWin

    Installation
  1. cygwin.com: Install Now > download + save setup.exe in c:/My Software Dumps
  2. Download Src: Download from Internet > Local Pkg Dir = c:\cygwin > Internet Connection = Direct Connection > Download site (eg. ftp.easynet.be) > Pkg Selectn > Install Dir = c:\cygwin
  3. Install from Local Dir > c:\cygwin
  4. zsh install: $ mkzsh -D -P (mk desktop + prog menu item); mkzsh -D -P -A (forall users); right-click (icon) > Properties > Colors
  5. Upgrade: Select Pkgs: Keep (retain existing pkgs); select new pkgs


III. zsh Install using cygwin under windows.

x. zshrc

# Cygwin .zshrc by Samar Abbas 12/09/2003

# Autocompletion Init Section

autoload -U compinit
compinit

# Spell Checking Section

setopt correct       # correct spelling of commands
setopt correct_all   # correct spelling of all args

# Auto Change Directory Section

setopt autocd autopushd 
setopt pushdignoredups

# Prompt Section
#   RPS1 (Right hand side Prompt Symbol): set to data and time

autoload -U promptinit
promptinit
prompt adam1 blue green green
RPS1=$'%{\e[1;31m%} {%D} %{\e[1;45m%} %{\e[2;33m%} <%*> %{\e[0m%}' 

# Auto list and menu selection Section

setopt auto_list # auto list choices in ambiguous selectn after 
setopt auto_menu # cycle menu options using 
setopt menu_complete # insert first matching entry in  list
setopt NO_bash_auto_list # overrides auto_list and forces 3x  press
setopt glob
setopt glob_complete # filename generation
setopt list_ambiguous 
setopt NO_clobber # don't overwrite existing files

# export DISPLAY to remotemachine (works in telnet only)

export DISPLAY=$REMOTEHOST:0.0 

# History options                                                              

HISTSIZE=2000
HISTFILE="$HOME/.zsh_history"
SAVEHIST=2000
setopt append_history
setopt inc_append_history
setopt share_history
setopt extended_history # beginning time:elapsed seconds:command added to history file
setopt hist_expire_dups_first

# Logout Options
setopt check_jobs # report job status before exit, twice exit ends
TMOUT=1800

# Custom Autocomplete List: host names
hosts=(alpha beta gamma
       cyberspace.org)
compctl -k hosts ssh scp ping host telnet

# Watch Section: Watch for Friend and Foe

WATCHFMT='%n %a %M %T'
LOGCHECK=3
export LOGCHECK
watch=all

# Alias Section

alias "textpad=C:/Program\\ Files/TextPad\\ 4/TextPad.exe"


%T  (date 24h)
%t = %@ (precise 12h time)
%D  (date in yy-mm-dd)
%*  (precise 24h time)
%w  (date day-dd)
%W  (date m/dd/yy)

%l  (current tty)
%h = %! (history no.)
%n  (username)
%U | %u (start/stop underline mode)
%S | %s (start/stop standout mode)
%B | %b (start/stop boldface mode)
%M  (full server name)
%m  (hostname upto first .)
%~  (pwd, with home dir = ~)
%/  (pwd)


VI. Shell Programming

a. Basic Shell Scripts

  1. Write the Shell Progam using an editor like pico or vi:

    hello.sh

    echo "Hello World"
    echo "From Samarstan"
    echo '\007\c'  # sysv bell; echo -n '^G' : bsd bell
    

  2. Execute Permission is to be granted : $chmod 755 hello.sh     if this is not done, "Permission denied" will be echoed once the script is executed.

  3. Execution of the Shell Script : Execute the script as if it were a command, giving its full path name, eg.
    $./hello.sh       where ./ refers to the present directory, or
    $bash hello.sh   or
    $"`pwd`/hello.sh"       where pwd returns the present working directory, or
    $set `pwd`=$PWD; $PWD/hello.sh   or
    $ ~/dir/subdir/hello.sh   using the full path name. In all cases, you should see "Hello World From Samarstan" printed to the screen.
basic.sh

#! /bin/bash  # magic nos: 1st 16 bits, tell kernel this is not bin exec
who
echo '******************'
ls
echo "******************"
# This is a comment
ps
echo '******************'

It is often customary to write the shell at the top of the script, as in #! /bin/bash , and # lines are comments.

b. Quotation Marks

The double quotation marks (""), the single quotation marks ('') or grave accents, and the backslash (\) are all used to hide special characters from the shell.

logname.sh

echo '******************'
echo "hello $LOGNAME"
echo "******************"

Note that using echo 'hello $LOGNAME' would not work, returning "hello there $LOGNAME" as the single quotes would hide the variable from the shell.

c. Variables

d. Conditional Statements

Perl, Sed


for.sh
#!/bin/sh
for i in 1 2 3 4 5
do
  echo "Looping ... number $i"
done
mkdir rc{0,1,2,3,4,5,6,S}.d

while.sh
#!/bin/sh
INPUT_STRING=hello
while [ "$INPUT_STRING" != "bye" ]
do
  echo "Please type something in (bye to quit)"
  read INPUT_STRING
  echo "You typed: $INPUT_STRING"
done

bash_expect.sh:
set password [index $argv 2]
spawn passwd [index $argv 1}
expect "*password:"
send "$password\r"
expect "*password:"
send "$password\r"
expect eof

bash_rename.sh
for f in *.html; do
   base=`basename $f .html`  # remove the suffix and its dot .html
   mv $f js_${base}.html     # add new prefix js_ and suffix
done

bash_while_lang.sh
#!/bin/sh
while read f
do
  case $f in
	hello)		echo English	;;
	howdy)		echo American	;;
	gday)		echo Australian	;;
	bonjour)	echo French	;;
	"guten tag")	echo German	;;
	*)		echo Unknown Language: $f		;;
   esac
done < myfile

multiple_wget.sh
#!/bin/sh
for i in 1 2 3 4 5;
do
   echo "Trying to execute wget -b http://www.yahoo${i}.com";
   wget -b http://www.yahoo${i}.com;
done;

wget_cmdline.sh

 #!/bin/bash
 for i in $( cat f.txt ); do
     echo item: $i
 done


cyclical_wget_hobsonjobson.sh

#! /bin/bash
(( maxC = 181 ))
(( i = 0 ))
while (( i < maxC ))
do
      echo "trying to get http://dsal.uchicago.edu/cgi-bin/ddsa/getobject_?.p.0:"$i"./projects/artfl0/databases/dicos/philologic/hobson/IMAGE/"
      wget "http://dsal.uchicago.edu/cgi-bin/ddsa/getobject_?.p.0:"$i"./projects/artfl0/databases/dicos/philologic/hobson/IMAGE/"
      (( i += 1 ))
done
echo completed




factorial.sh
#!/bin/sh

factorial()
{
  if [ "$1" -gt "1" ]; then
    i=`expr $1 - 1`
    j=`factorial $i`
    k=`expr $1 \* $j`
    echo $k
  else
    echo 1
  fi
}

while :
do
  echo "Enter a number:"
  read x
  factorial $x
done                      

Various script headers:
#!/bin/sh
#!/bin/bash
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/usr/awk -f


test.sh
#!/bin/sh
if [ "$X" -lt "0" ]
then
  echo "X is less than zero"
fi
if [ "$X" -gt "0" ]; then
  echo "X is more than zero"
fi
[ "$X" -le "0" ] && echo "X is less than or equal to  zero"
[ "$X" -ge "0" ] && echo "X is more than or equal to zero"
[ "$X" = "0" ] && echo "X is the string or number \"0\""
[ "$X" = "hello" ] && echo "X matches the string \"hello\""
[ "$X" != "hello" ] && echo "X is not the string \"hello\""
[ -n "$X" ] && echo "X is of nonzero length"
[ -f "$X" ] && echo "X is the path of a real file" || echo "No such
file: $X"
[ -x "$X" ] && echo "X is the path of an executable file"
[ "$X" -nt "/etc/passwd" ] && echo "X is a file which is newer than
/etc/passwd" 

bash_mkfifo.sh
#!/bin/bash
# ==> Script by James R. Van Zandt, and used here with his permission.
# ==> Comments added by author of this document.

  
  HERE=`uname -n`    # ==> hostname
  THERE=bilbo
  echo "starting remote backup to $THERE at `date +%r`"
  # ==> `date +%r` returns time in 12-hour format, i.e. "08:08:34 PM".
  
  # make sure /pipe really is a pipe and not a plain file
  rm -rf /pipe
  mkfifo /pipe       # ==> Create a "named pipe", named "/pipe".
  
  # ==> 'su xyz' runs commands as user "xyz".
  # ==> 'ssh' invokes secure shell (remote login client).
  su xyz -c "ssh $THERE \"cat >/home/xyz/backup/${HERE}-daily.tar.gz\" < /pipe"&
  cd /
  tar -czf - bin boot dev etc home info lib man root sbin share usr var >/pipe
  # ==> Uses named pipe, /pipe, to communicate between processes:
  # ==> 'tar/gzip' writes to /pipe and 'ssh' reads from /pipe.

  # ==> The end result is this backs up the main directories, from / on down.

  # ==> What are the advantages of a "named pipe" in this situation,
  # ==> as opposed to an "anonymous pipe", with |?
  # ==> Will an anonymous pipe even work here?
 exit 0

Libraries


xlib.lib
# xlib.lib
# Note no #!/bin/sh as this should not spawn an extra shell.
# It's not the end of the world to have one, but clearer not to.
#
STD_MSG="About to rename some files..."
rename()
{
  # expects to be called as: rename .txt .bak 
  FROM=$1
  TO=$2

  for i in *$FROM
  do
    j=`basename $i $FROM`
    mv $i ${j}$TO
  done
}

f_xlib.sh
#!/bin/sh
# function2.sh
. ./xlib.lib
echo $STD_MSG
rename txt bak

Exit codes: a no. 0-255 returned by any Unix command when it returns control to parent process. success = exit 0; failure = non-zero exit code.


exit.sh
#!/bin/sh
# Second attempt at checking return codes
grep "^${1}:" /etc/passwd > /dev/null 2>&1
if [ "$?" -ne "0" ]; then
  echo "Sorry, cannot find user ${1} in /etc/passwd"
  exit 1
fi
USERNAME=`grep "^${1}:" /etc/passwd|cut -d":" -f1`
NAME=`grep "^${1}:" /etc/passwd|cut -d":" -f5`
HOMEDIR=`grep "^${1}:" /etc/passwd|cut -d":" -f6`

echo "USERNAME: $USERNAME"
echo "NAME: $NAME"
echo "HOMEDIR: $HOMEDIR"

telnet.sh: $ ./telnet.sh | telnet
#!/bin/sh
host=127.0.0.1
port=23
login=steve
passwd=hellothere
cmd="ls /tmp"

echo open ${host} ${port}
sleep 1
echo ${login}
sleep 1
echo ${passwd}
sleep 1
echo ${cmd}
sleep 1
echo exit

append.sh
for x in `ls`; do  # foreach file (`ls -1`)
#  {echo "some text" ; cat f} > g; mv g f
  (cat header.txt; cat $x) > g; mv g $x
  cat footer.txt >> $x
done
# echo -e "12345\n$(cat tt)" > tt
# sed '1i\\
# hello world' tt >tt.out
# delete lines by no or match usg sed


IV. Converting DOS Batch Files to Shell Scripts

Table: Batch file keywords / variables / operators, and their shell equivalents
Batch File OperatorShell Script EquivalentMeaning
%$command-line parameter prefix
/-command option flag
\/directory path separator
===(equal-to) string comparison test
!==!!=(not equal-to) string comparison test
||pipe
@set +vdo not echo current command
**filename "wild card"
>>file redirection (overwrite)
>>>>file redirection (append)
<<redirect stdin
%VAR%$VARenvironmental variable
REM#comment
NOT!negate following test
NUL/dev/null"black hole" for burying command output
ECHOechoecho (many more option in Bash)
ECHO.echoecho blank line
ECHO OFF set +vdo not echo command(s) following
FOR %%VAR IN (LIST) DOfor var in [list]; do"for" loop
:LABELnone (unnecessary)label
GOTOnone (use a function)jump to another location in the script
PAUSEsleeppause or wait an interval
CHOICEcase or selectmenu choice
IFifif-test
IF EXIST FILENAME if [ -e filename ]test if file exists
IF !%N==!if [ -z "$N" ]if replaceable parameter "N" not present
CALLsource or . (dot operator)"include" another script
COMMAND /Csource or . (dot operator) "include" another script (same as CALL)
SETexportset an environmental variable
SHIFTshiftleft shift command-line argument list
SGN-lt or -gtsign (of integer)
ERRORLEVEL$?exit status
CONstdin"console" (stdin)
PRN/dev/lp0(generic) printer device
LPT1/dev/lp0first printer device
COM1/dev/ttyS0first serial port


References