|
Submit a new Tip
|
|
Top
"comment out" code blocks
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
One line of shell code can be "commented out" using the
"#" character. Sometimes however it would be nice to "comment out"
more than one line of code, like the C "/* */" comments.
One way to comment out multiple lines is this:
: '
,,,,,,
'
After the ":" command (that returns "true") the rest of the code
is a large string constant enclosed within 'single quotes'.
Of course this works only if the code "commented-out" does
not contain single quotes.
|
Top
"comment out" code blocks (2)
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
Another way to comment out multiple lines of code is the "here document"
way:
: << --END-COMMENT--
your comment here
--END-COMMENT--
This way, there is no restriction with single quotes. Note that any delimiter
can be used in place of "--END-COMMENT--".
(tested with bash 2.05a)
|
Top
"comment out" code blocks (3)
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
(tested in bash 3 only)
: << --END-COMMENT--
your comment here
--END-COMMENT--
could cause problem if `somecommand` or $(somecommand) is used.
: << --END-COMMENT--
`touch /tmp/foo`
foo bar
--END-COMMENT--
instead
: << '--END-COMMENT--'
`touch /tmp/foo`
foo bar
--END-COMMENT--
Additionally : can use also not-interpreted commands, e.g.
: INGORED UP TO << '--END-COMMENT--'
`touch /tmp/foo`
foo bar
--END-COMMENT--
|
Top
cleaning up tmp files
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
I've seen too many scripts using massive number of tmp files, which
is wrong in its self.
But not only that, people tend to clean them up one at a time in a fashion
such as
if [ -a ${tmpFile} ]; then
rm ${tmpFile};
fi
This, when you use up to 10 or even 5 tmp files gets nasty. A quicker way
of cleaning
up such tmp files is to use a simple loop, I even perfer to use array's
which are
availible in Korn shell. Here is an example.
clean()
{
tmpfiles[0]=${temp1}
tmpfiles[1]=${temp2}
for file in ${tmpfiles[*]}
do
if [ -a ${file} ]; then
rm ${file}
fi
done
This way, as you accumulate more and more tmp files, you need only to add
one line to get it cleaned up.
|
Top
cleaning up tmp files (2)
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
Another way to clean up multiple temporary
files is to create them within a subdirectory, i.e.
TmpBase=${TMPDIR:=/tmp}/myscript.$$
mkdir "$TmpBase" || exit 1 # create directory
chmod 700 "$TmpBase" || exit 1 # restrict access
# Remove all temporary files after program termination
# or at receiption of a signal:
trap 'rm -rf "$TmpBase" >/dev/null 2>&1' 0
trap "exit 2" 1 2 3 15
# The following files will be remove automatically:
input=$TmpBase/input
output=$TmpBase/output
#...
|
Top
Convert "relative" in "absolute" path name
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
In shell scripts it is often necessary to convert a
relative path name, i.e. "../usr/../lib/somefile" to
an absolute path name starting with a slash "/", i.e.
"/lib/somefile". The following code fragment does exactly this:
D=`dirname "$relpath"`
B=`basename "$relpath"`
abspath="`cd \"$D\" 2>/dev/null && pwd || echo \"$D\"`/$B"
|
Top
Positioning the cursor from within shell scripts
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
[This tip was first published within the SHELLdorado Newsletter 1/99]
For some shell scripts it would be desirable, if the script
could position the cursor to arbitrary (row, column) pairs
(i.e. to display a status line, ...)
The following shell function uses the "tput" command to
move the cursor to the specified (row, column) position:
# move cursor to row $1, col $2 (both starting with zero)
# usage: writeyx message rowno colno
writeyx () {
tput cup $2 $3
echo "$1"
}
Example usage:
clear # clear the screen
writeyx "This is a centered message" 11 26
writeyx "press any key to continue..." 22 0
read dummy
The "tput" comm!
and looks up the escape command sequence for
a feature needed for the current terminal. You can use it
for other terminal related things, too:
tput smso # "start mode shift out": usually
# reverse
echo "This is printed reverse"
tput rmso # "reset mode shift out"
All available capability names are listed on the terminfo(5)
manual page.
Portability:
The "tput" command is available with the "terminfo"
terminal information database
|
Top
Setting default values for variables
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
In shell scripts it's often useful to
provide default values for script variables, i.e.
if [ -z "$Host" ]
then
Host=`uname -n`
fi
For this kind of assignment the shell
has a shorthand:
: ${Host:=`uname -n`}
This means: if the variable "Host" is
not already set, execute the command
"uname -n" and set the variable to
the returned value.
|
Top
Getting a file into "memory"
|
Level: Script Programmer |
Submitted by:
glong@openwave.com
|
URL:
none
|
Sometimes its convienient to have a file
read into memory to work on it. The
form that you take to accomplish this is
an array data structure. In ksh88 the
maximum is 1024 elements, however, on
some of the more modern versions you can
go much higher.
To do this the following can be done:
#/usr/bin/ksh
typeset -i cnt=0
while read line
do
myarray[$cnt]=$line
((cnt = cnt + 1))
done < myfile
# end of file---------
Now, if I want to access any line of that
file, I simply use:
${<arrayname>[<subscript>]}
echo ${myarray[4]}
This is useful for parsing, or for interactive
use of of the file's contents. I have
all !
of the lines of the file available in
the array, and I can move around in them,
select the ones I want.
Look at the following example:
#!/usr/bin/ksh
typeset -i cnt=0
while read line
do
myarray[$cnt]=$line
((cnt = cnt + 1))
done < myfile
PS3="Select a number: "
select linefromfile in ${myarray[@]}
do
echo $linefromfile
done
# end of file------------
There are many other uses for this techique.
Dynamic menus
numeric error message reference
getting mulitple specific lines of a file
in a single pass
Have fun.
|
Top
Find user's name
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
The full name of each user is available in the /etc/passwd file. If you
would like to use the full name in your script instead of $LOGNAME,
which simply returns the user's login name, you can declare the following
variable in your script:
fullname=`grep $LOGNAME /etc/passwd | cut -f 5 -d :`
If you only want the first name, you would declare this variable:
firstname=`grep $LOGNAME /etc/passwd | cut -f 5 -d : | cut -f 1 -d " "`
|
Top
Find user's name (2)
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
Since the full name is the 5th column
of the file /etc/passwd, it's easy
to look up the full name for a
login name like "joe":
awk -F: '$1 == name {print $5}' name=joe /etc/passwd
The option "-F" tells awk to use ":" als field
separator (instead of whitespace).
|
Top
Using "here-documents" instead of multiple "echo"
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
Multiple "echo" commands may be replaced by a "here-document".
This makes the script faster and easier to read.
Example:
echo "Please enter your choice:"
echo "1 - list current directory"
echo "2 - list current users"
echo "3 - log off"
may be replaced with
cat <<!
Please enter your choice
1 - list current directory
2 - list current users
3 - log off
!
|
Top
Using "here-documents" instead of multiple "echo" (2)
|
Level: Script Programmer |
Submitted by:
bijoytg@usa.net
|
URL:
http://www.geocities.com/bijoytg_
|
# you can also turn multi-echos into a single echo
echo "
Welcome to Foo.Bar v0.8
=======================
Press enter to continue...
";
|
Top
To find idle users
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
w | gawk '
BEGIN { FIELDWIDTHS = "9 11 13 10 8 7 7 14" }
NR > 2 {
idle = $5
sub(/^ */, "", idle)
if ( idle == "" )
idle = 0
if (idle ~ /:/) {
split(idle, t, ":")
idle = t[1] * 60 + t[2] #Converts idle time into seconds
}
if (idle ~ /days/)
idle *= 24*60*60
print $1, $2, idle
}'
|
Top
Using ksh builtins instead of external commands
|
Level: Script Programmer |
Submitted by:
unix-guy@pacbell.net
|
URL:
http://www.community.net/~atomik
|
Many times, scripters will use external commands like basename, dirname and
tr because they don't realize they can instead use ksh builtins.
An added bonus is the builtins are faster and require less system resources
because no sub-process is spawned.
basename replacement:
---------------------
$ fullfile="/some/dir/file.txt"
# replaced: file=$(basename $fullfile)
$ file=${fullfile##*/}
$ echo $file
file.txt
dirname replacement:
--------------------
$ fullfile="/some/dir/file.txt"
# replaced: dir=$(dirname $fullfile)
$ dir=${fullfile%/*}
$ echo $dir
/some/dir
tr replacements:
----------------
$ word="MiXeD"
# replaced: word=$(echo $word | tr [A-Z] [a-z])
$ typeset -l word
$ echo $word
mixed
# replaced: word=$(echo $word | tr [a-z] [A-Z])
$ typeset -u word
$ echo $word
MIXED
|
Top
KSH build-in networking functions
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
[Note: the following examples will work only with standard
ksh implementations. They will not work with the Linux Korn
Shell pdksh.]
Most Korn Shells (/bin/ksh) have sparsely documented, build-in
networking functions.
Example:
$ date=
$ read date < /dev/tcp/127.0.0.1/13
$ echo $date
Wed Feb 10 00:45:39 MET 1999
This command opens a TCP connection to the IP address 127.0.0.1
(the local loopback IP address), and connects to the port "13"
(daytime, see /etc/services). The current date and time is
returned, and assigned to the variable "date".
Note that the "/dev/tcp/*" directories do not have to exist;
the file names are special to the Korn Shell and are interpre!
ted
by the shell internally. Only numerical ip addresses and port
numbers are supported; "read date < /dev/tcp/localhost/daytime"
does not work.
|
Top
"Normalize" input field separators
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
Script programmers sometimes have to process input that consists
of fields separated by whitespace, i.e.
field1 field 2 <TAB> <TAB> field3
This input has the disadvantage that it uses different combinations
of blank and TAB characters as input, and is hard to process using
"cut" and "sort", because these commands expect exactly one
field separator character.
The following "sed" line "normalizes" this input replacing each
sequence of two or more whitespace characters with exactly one
<TAB> character:
sed 's/ [ <TAB>][ <TAB>]*/<TAB>/g'
Substitute the five characters "<TAB>" with a "real" TAB character
(ASCII 9).
Further processing can be done using this <TAB> character
as field separator.
|
Top
To Reverse a File
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
######## TO PRINT FILE IN REVERSE ORDER BY LINE #############3
if [ $# -ne 1 ]
then
echo "Usage reverse_file <filename> "
exit 1;
fi
######## By using for loop #############
awk '{ line[NR] = $0 } END { for (i=NR; i>0; i=i-1)
print line[i] }' $1
|
Top
Script debugging settings
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
Most shell script programmers know the command
set -vx
to print each script command before execution. Sometimes
the following flags are useful, too:
set -e # terminate the script at first error
set -u # unset variables are fatal errors
|
Top
Enable "set -x" for all functions
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
Tracing can be enabled for a
KornShell script by using "set -x", but
this will have no effect on functions
within the script.
The following line will enable tracing
for all functions in a script:
typeset -ft $(typeset +f)
Note that this will not work for BASH.
[Idea is from Dan Mercer <dmercer@mn.rr.com>,
news group comp.unix.shell]
|
Top
Swapping stdout and stderr
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
In scripts it's often useful not
to filter standard output (i.e. cmd | filter),
but stderr. This may be done using
the following command:
cmd 3>&1 1>&2 2>&3
It's even possible to filter both
standard output and standard error:
( ( cmd | ... process stdout ) 3>&1 1>&2 2>&3 ) | \
... process stderr 3>&1 1>&2 2>&3
The last file descriptor operations restore
the normal meaning of stdin and stdout.
|
Top
Quickly comment/uncomment whole scripts
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
#!/usr/bin/ksh
#set -x
#-----VARIABLES
# You must comment out one of the two VAR lines below depending on whether
you want comments turned on or off in front of all your executable commands.
# The VAR line below, when uncommented, will comment out all commands with
$VAR in front of them
#VAR=": #"; export VAR
# The VAR line below, when uncommented, will allow execution of all commands
with $VAR in fron of them.
VAR=""; export VAR
#-----MAIN
$VAR date
|
Top
Find yesterday's date
|
Level: Script Programmer |
Submitted by:
???
|
URL:
none
|
A simple way, to find out yesterdays date:
#!/usr/bin/ksh
############################
### set the TZ env variable
############################
TZ=GMT+24
/usr/bin/date +%m%d%Y
TZ specifies the time zone in which the time and date are written. If you
set the time zone 24 hours forward, date will give yesterdays date back
|
|
|