Hi All,
Presently I am updating my blog: http://unstableme.blogspot.com/ , which is a pure BASH blog, mainly focussed on one liners with SED, AWK and BASH.
//Jadu
rm -- -C
To set a default value for a BASH variable, the syntax is: (good for setting default value for a BASH command line parameter)
VARIABLE=${1:-DEFAULTVALUE} #set VARIABLE with the value of 1st Arg to the script,
The following simple script illustrate this:
tmpdir=/tmp
defvalue=1
DIR=${1:-$tmpdir} # Defaults to /tmp dir.
VALUE=${2:-$defvalue} # Default value is 1.
echo $DIR
echo $VALUE
Now while running the script, specify values for both the arguments.
$ ./defvaue.sh /dev 23
/dev
23
This time don't mention their values.
$ ./defvaue.sh
/tmp
1
so
DIR=${1:-$tmpdir}
VALUE=${2:-$defvalue}
is a replacement for the following:
[ -z $1 ] && DIR="/tmp"
[ -z $2 ] && VALUE=1
To start with, first lets discuss some of the basic controls available in "dialog", later using them we will develop a simple application.
A copy-paste from man pages of dialog:
Usage: dialog --clear
dialog --create-rc <file>
dialog [--title <title>] [--separate-output] [--backtitle <backtitle>] [--clear] <Box options>
Box options:
--yesno <text> <height> <width>
--msgbox <text> <height> <width>
--infobox <text> <height> <width>
--inputbox <text> <height> <width> [<init>]
--textbox <file> <height> <width>
--menu <text> <height> <width> <menu height> <tag1> <item1>...
--checklist <text> <height> <width> <list height> <tag1> <item1> <status1>...
--radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>...
--guage <text> <height> <width> <percent>
--file <dir> <height> <width> <mode> [<init>]
CHECKLIST
dialog --checklist "Choose OS:" 10 40 5 \
1 Linux on \
2 Solaris off \
3 "HP UX" off \
4 AIX off

INPUTBOX
dialog --title "Inputbox - Example" --backtitle "www.jaduks.livejournal.com" \
--inputbox "Enter your favourite OS here" 8 60

MENU
dialog --title "A dialog Menu Example" \
--menu "Please choose the command:" 15 55 5 \
"nestat -r" "Display the kernel routing tables" \
"netstat -a" "listening / non-listening sockets" \
"/sbin/ifconfig" "configure a network interface"

MSGBOX
dialog --title "Example Dialog message box" \
--msgbox "\n Installation Completed on 172.22.23.124" 6 50

RADIOLIST
dialog --backtitle "Flims" \
--radiolist "Select Flim:" 10 40 3 \
1 "Life is beautiful" off \
2 "Beautiful Mind" on \
3 "Walk in the clouds" off

To print all the files from 19-10-2007 to 21-11-2007
$ find <DIR> -type f -exec ls -l --time-style=full-iso {} \; | awk '{print $6,$NF}' | awk '{gsub(/-/,"",$1);print}' | awk '$1>= 20071019 && $1<= 20071121 {print $2}'
And to print all files from 2nd Nov 2007 to today, (Using -mtime with find command, and calculating the mtime based on the epoch times.)
$ FROM=`date --date='11/2/2007' +%s`
$ TO=`date +%s`
$ find /home/jsaikia/harness/rough/ -type f -mtime -`echo "(((($TO - $FROM ) / 24) / 60) / 60)" | bc` -print
If anyone got any better idea, please put in the comment section below.
Assume your application generates some output in a file named "fan.out" in the following format.
FAN_NAME|TIME|RPM|STATUS
FAN1|1195699322|4566|Moderate Condition
FAN12|1195699112|3562|Bad Condition
FAN11|1195699321|5676|Good Condition
FAN15|1195699117|8923|Good Condition
..
..
You want these data to be automatically pushed to your mysql "FAN.DETAILS" table through a BASH script.
This is how I did that.
Step1: Created the database and table in mysql as below.

so the db is ready.
Step2: From the above fan.out, my idea for creating the corresponding .sql
$ sed -e 1d -e 's/|/","/g' -e 's/^/insert into FAN.DETAILS values ("/' -e 's/$/");/' fan.out > /tmp/fan.out.sql
insert into FAN.DETAILS values ("FAN1","1195699322","4566","Moderate Condition");
insert into FAN.DETAILS values ("FAN12","1195699112","3562","Bad Condition");
insert into FAN.DETAILS values ("FAN11","1195699321","5676","Good Condition");
insert into FAN.DETAILS values ("FAN15","1195699117","8923","Good Condition");
Step3: Here my script "push2mysql.sh" is ready
#!/bin/bash
#http://jaduks.livejournal.com/
SQLFILE=/tmp/fan.out.sql
trap "rm -f $SQLFILE" EXIT
MYUSER=root
INPUTF=./fan.out
TABLE="FAN.DETAILS"
sed -e 1d -e 's/|/","/g' -e 's/^/insert into '"$TABLE"' values ("/' -e 's/$/");/' $INPUTF > $SQLFILE
echo "quit" >> $SQLFILE
/usr/local/mysql/bin/mysql -u $MYUSER < $SQLFILE
echo "Completed"
<A Sample RUN>
$ ./push2mysql.sh
Completed
<FROM MYSQL>

Hope you like this post :-) Got to go to office, getting late.
$ echo - | awk '{print "1\t2\t3\n4\t5\t6\n7\t8\t9\n"}'
1 2 3
4 5 6
7 8 9
$ echo - | awk '{print "1\t2\t3\n4\t5\t6\n7\t8\t9\n"}' | awk '{for(j=1;j<=NF;j++){arr[j]=arr[j]"\t"$j}
1 4 7
2 5 8
3 6 9
BASH got this feature as well, background and foreground color options for text. Here is a short discussion on the color codes and their use.


Note:
echo -e '\E[COLOR1;COLOR2m THE TEXT.'
tput sgr0 = > Reset text attributes to normal without clear.
The Following BASH code will print all the color combinations:
#!/bin/bash
for i in `seq 30 37`
do
for j in `seq 40 47`
do
echo -e '\E['$i';'$j'm TEXT'
tput sgr0
done
done
Using BASH, here I present before you a chess board, :-)

The Code for generating the above chess board is :
#!/bin/sh
for m in `seq 8` ### Outer for loop ###
do
for n in `seq 8` ### Inner for loop ###
do
T=`expr $m + $n`
S=`expr $T % 2`
done
echo -e -n "\033[40m" #### background colour=black ###
echo "" #### printing new line ###
done
Using sed we can Add/Change/Insert lines in a file, I found it very useful, hope you too !
$ cat namedb.txt
Nina:
Apen:
Lokesh:
#Add a line
a)
$ sed '
/Apen/ a\
Add this line after Apen
' namedb.txt
<Output>
Nina:
Apen:
Add this line after Apen
Lokesh:
b)
$ sed '
2 a\
Add this line after 2nd line
' namedb.txt
<Output>
Nina:
Apen:
Add this line after 2nd line
Lokesh:
#Insert a new line before
c)
$ sed '
/Apen/ i\
Insert this line after Apen
' namedb.txt
<Output>
Nina:
Insert this line after Apen
Apen:
Lokesh:
Similary one can mention the line number (case b above)
#Change a line
d)
$ sed '
/Apen/ c\
Change the line with Apen to this line
' namedb.txt
<output>
Nina:
Change the line with Apen to this line
Lokesh:
Similarly one can mention the line number to change (case b above)
Simple way of writing log functions in a shell script.
#!/bin/sh
....
....
LOGFILE=./log.`date +%s`.out
#LOG functions
f_LOG() {
echo "`date`:$@" >> $LOGFILE
}
f_INFO() {
echo "$@"
f_LOG "INFO: $@"
}
f_WARNING() {
echo "$@"
f_LOG "WARNING: $@"
}
f_INFO "Checking 0 kb files"
...
... calculation for 0 kb file checking
...
f_WARNING "There are $BLFILES 0 kb files found, this can cause problem sometime, still continuing .."
Now
$ cat log.1194832036.out
Mon Nov 12 07:17:16 IST 2007:INFO: Checking 0 kb files
Mon Nov 12 07:17:16 IST 2007:WARNING: There are 3 0 kb files found, this can cause problem sometime, still continuing ..

Apart from using "sleep", here something you can do with BASH to puase something until user responds!
$ cat pause.sh
f_pause()
{
key=""
echo -n "Press any key to continue.."
stty -icanon
key=`dd count=1 2>/dev/null`
stty icanon
}
echo "I am here"
f_pause
echo "I am at the end"
Another way :
$ cat pause1.sh
f_pause(){
read -p "$*"
}
echo "I am here"
f_pause "Hit Enter key to continue."
echo "I am in the last line"
seq is a very useful command to generate sequence of numbers. Some of its uses are discussed below.
<Printing from 1 to 5>
$ seq 5
1
2
3
4
5
<Printing from 2 to 5>
$ seq 2 5
2
3
4
5
<Printing from 2 to 12, with 3 increment,FIRST INCREMENT LAST>
$ seq 2 3 12
2
5
8
11
<-s, --separator>
$ seq -s : 1 5
1:2:3:4:5
<Adding 1 to 10>
$ seq -s " + " 1 10
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
$ expr `seq -s " + " 1 10`
55
<-w, --equal-width>
$ seq -s : -w 1 10
01:02:03:04:05:06:07:08:09:10
<Use with for loop>
$ for i in `seq 5`
> do
> echo "square of $i is `expr $i \* $i`"
> done
square of 1 is 1
square of 2 is 4
square of 3 is 9
square of 4 is 16
square of 5 is 25
which is a replacement of :
1) for i in 1 2 3 4 5
or
2) for ((i=1;i<=5;i++))

Q: I have written a lengthy shell script (more than 1000 lines) and all the sys.admins in my company use that script and it runs as
root. My concern is what if some sys.admin just add a "rm -r /" in the script which might jeopardize my whole efforts. Is there a way to
convert a shell Script to an exe file ? or some other way so that others cannot see the code ? If u know pls. share.
A:
Generic shell script compiler(shc) creates a stripped binary executable version of the script specified with -f on the command line. (shc encrypts shell scripts using RC4)
Get SHC from : http://www.datsi.fi.upm.es/~frosal/
DEBIAN: apt-get install shc
$ shc -f sundays.sh
It will generate following two files
sundays.sh.x.c # Generated C source code of sundays.sh
sundays.sh.x # The binary version of sundays.sh
share "sundays.sh.x" with your friends, which is a executable and encrypted one.
Note: Paranoid Penguin - Limitations of shc, a Shell Encryption Utility
A) <Removing Blank Lines>
$ cat test.out
this is line 1
this is line 2
some lines here
there are 2 blank lines above
this is last line
$ grep -v '^$' test.out
$ grep '.' test.out
$ sed '/^$/d' test.out
$ sed -n '/^$/!p' test.out
$ awk NF test.out
$ awk '/./' test.out
$ cat test1.out
this is line 1
this is line 2
some lines here
this is last line
B) <Lines that doesn't contain a particular regexp>
$ grep -v 'this' test1.out
$ awk '!/this/' test1.out
$ sed '/this/d' test1.out
$ sed -n '/this/!p' test1.out
will print
some lines here
<Printing last Line>
$ tail -1 test1.out
$ sed '$!d' test1.out
$ sed -n '$p' test1.out
$ awk 'END {print}' test1.out
will print
this is last line
C) <Printing first line>
$ head -1 test1.out
$ sed q test1.out
$ awk 'NR>1 {exit};1' test1.out
will print
this is line 1
D) <Number of lines in a file>
$ wc -l test1.out | cut -d " " -f1
$ sed -n '$=' test1.out
$ awk 'END {print NR}' test1.out
E) <Printing first 2 lines >
$ awk 'NR<3' test1.out
$ head -2 test1.out
$ sed '1,2 p' test1.out
$ cat names.out
Lehe
Alex
Themo
Demo
Saik
F) <Print the line immediately before a regex, but not the line containing the regex>
$ awk '/Alex/ {print x};{x=$0}' names.out
$ sed -n '/Alex/{g;1!p;};h' names.out
$ S=`grep -n Alex names.out | awk -F : '{print $1}'`; S=`expr $S - 1`; sed -n "$S p" names.out
will print
Lehe
G) <print the line immediately after a regexp, but not the line containing the regexp>
$ sed -n '/Lehe/{n;p;}' names.out
$ awk '/Lehe/{getline;print}' names.out
$ S=`grep -n Lehe names.out | awk -F : '{print $1}'`; S=`expr $S + 1`; sed -n "$S p" names.out
will print
Alex
There is an option with "read" to read the passwords, here is a small shell script stating that.
When the script prompt to enter the password, and user type the password it won't be displayed on the screen.
$ cat passwwd.sh
#!/bin/sh
ACTUAL="jksaikia"
read -s -p "Password: " enteredpass
echo ""
[ "$enteredpass" == "$ACTUAL" ] && echo "Accepted" || echo "Sorry"
$ ./passwwd.sh
Password:
Accepted
$ ./passwwd.sh
Password:
Sorry
This can be achieved using "stty" also as shown below:
old_set=`stty -g` #store original state of stty
echo -n "Password: "
stty -echo #turn off echoing
read password
stty echo #To restore the echoing
echo ""
echo "You entered $password"
stty $old_set #set original state
Useful, simple though!
$ cat sal.out
A Mon clerk 12
B Tue sales 13
A Wed clerk 13
C Thu sales 34
B Mon sales 13
$ awk '{arr[$1]+=$4} END {for (i in arr) {print i,arr[i]}}' sal.out
A 25
B 26
C 34

"workingdet" is the file which contains the number of hours each employee has worked.
The fields are:
Emloyee Month Designation HoursWorked
$ cat workingdet
A Jan clerk (02:45)
B Jan Salesman (02:12)
C Jan Accountant (03:12)
A Feb clerk (01:10)
B Feb Salesman (11:10)
B March Salesman (3:10)
C Feb Accountant (3:34)
Here is something with AWK to calculate the total hours each Employee worked.
$ cat workingdet | awk '{print $0 ":" $4}' | sed 's/(//g'| sed 's/)//g' | awk -F ":" '{x=$3*60 + $4 ;print $0 " " x}' | awk '{emp[$1]+=$NF} END{for(i in emp){print i,"Total hours",emp[i],"mins";}}'
A Total hours 235 mins
B Total hours 992 mins
C Total hours 406 mins
$ cat workingdet | awk '{print $0 ":" $4}' | sed 's/(//g'| sed 's/)//g' | awk -F ":" '{x=$3*60 + $4 ;print $0 " " x}' | awk '{emp[$1]+=$NF} END{for(i in emp){printf "%c Total Hours %d:%d",i,emp[i]/60,emp[i]%60;printf "\n"}}'
A Total Hours 3:55
B Total Hours 16:32
C Total Hours 6:46
NOYEAR=65
YEAR=$1
f_Usage()
{
echo "Usage: `basename $0` <Year>"
}
[ -z $YEAR ] && f_Usage && exit $NOYEAR
for j in `seq 12`
do
if [ `cal $j $YEAR | sed -n '3p' | wc -w` -lt 7 ]
then
cal $j $YEAR |sed -e 3d -e 2d -e '/^$/d'|awk '{print $1}'|while read i; do echo -n $i; echo -n "/"; done
else
cal $j $$YEAR |sed -e 2d -e '/^$/d'|awk '{print $1}'|while read i; do echo -n $i; echo -n "/"; done
fi
echo
done
<Run>
[jsaikia] ~/prac $ ./sundays 2008
January/6/13/20/27/
February/3/10/17/24/
March/2/9/16/23/30/
April/6/13/20/27/
May/4/11/18/25/
June/1/7/14/21/28/
July/6/13/20/27/
August/3/10/17/24/31/
September/7/14/21/28/
October/5/12/19/26/
November/2/9/16/23/30/
December/7/14/21/28/
This is very important while one configure snmp simulator "mimic" for telnet.
Here is one script to simulate "show" command, important, simple though.
#!/bin/sh
#show
count=0
FILE=./telnet/
for arg in $*
do
count=`expr $count + 1`
if [ $count -lt $# ]
then
FILE="$FILE"$arg"_"
else
FILE="$FILE"$arg
fi
done
cat $FILE.txt
A Sample run:
[jadu@GILBERT] Happy $ ./show ospf neighbor
cat: ./telnet/ospf_neighbor.txt: No such file or directory
So actual "show ospf neighbor" output is to be kept in the file ./telnet/ospf_neighbor.txt.
Method 1:
[jsaikia] ~/prac/blog $ find patch/ -name *.txt -type f -print -exec cp {} {}.bak \;
Method 2:
[jsaikia] ~/prac/blog $ find patch/ -name *.txt -type f -print | while read file
> do
> cp $file $file.bak
> done
In order to open the man page of any command in vim editor use the following command:
[jadu@GILBERT] opt $ man ls | ul -i | vi -
Note: ul turns the underline and bold escape codes into readable format
This is always a day-2-day task, taking backups of all the files, and removing backup extn from the files sometime.
Here is something to do that.
[jsaikia] ~/prac/blog/patch $ ls
as.txt.bak bs.txt.bak cs.txt.bak
[jsaikia] ~/prac/blog/patch $ ls | while read file
> do
> mv $file `echo $file | sed 's/.bak$//g'`
> done
One more way:
[jsaikia] ~/prac/blog/patch $ ls | while read file
> do
> mv $file `basename $file .bak`
> done
[jsaikia] ~/prac/blog/patch $ ls
as.txt bs.txt cs.txt
And if you have to do the above operation with all the files in the present as well as sub-dirs , use the same with "find", this helps.
[jsaikia] ~/prac/blog $ find patch/ -name *.bak -type f -print
patch/dir2/ds.txt.bak
patch/as.txt.bak
patch/bs.txt.bak
patch/cs.txt.bak
[jsaikia] ~/prac/blog $ find patch/ -name *.bak -type f -print | while read file
> do
> mv $file `echo $file | sed 's/.bak$//g'`
> done
[jadu@GILBERT] LOLA $ ls
as.html bs.html cs.html
[jadu@GILBERT] LOLA $ ls | while read file
> do
> mv $file `basename $file .html`.doc
> done
[jadu@GILBERT] LOLA $ ls
as.doc bs.doc cs.doc
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X1
Now to
the PATH:
$ echo $PATH | awk -F : '{print "PATH is set to\n++++++++++++++"} {for (i=1;i<=NF;i++) {print "["i"]",$i}}'
PATH is set to
++++++++++++++
[1] /usr/local/bin
[2] /usr/bin
[3] /bin
[4] /usr/bin/X11
[5] /usr/games
[6] /home/jsaikia/impscripts/
Another simple way of doing it:
$ echo $PATH | tr ':' '\n' | awk '{print "["NR"]"$0}'
[1]/usr/local/bin
[2]/usr/bin
[3]/bin
[4]/usr/bin/X11
[5]/usr/games
[6]/home/jsaikia/impscripts/
Make this a function in .bash_profile or .profile.
mypath ()
{
echo $PATH | tr ':' '\n' | awk '{print "["NR"]"$0}'
}
which will split out the elements of a PATH, one per line.
$ mypath
[1]/usr/local/bin
[2]/usr/bin
[3]/bin
[4]/usr/bin/X11
[5]/usr/games
[6]/home/jsaikia/impscripts/
Sometime whenever we query for some process:
bash-2.03$ ps -ef | grep console
root 350 1 0 Apr 22 console 0:00 /usr/lib/saf/ttymon -g -h -p huey console login: -T sun -d /dev/console -l con
the process detail is incomplete (up-to con above)
To get the full length view:
use this command: /usr/ucb/ps auxww [PID]
bash-2.03$ /usr/ucb/ps auxww 350
USER PID %CPU %MEM SZ RSS TT S START TIME COMMAND
root 350 0.0 0.0 1880 8 console S Apr 22 0:00 /usr/lib/saf/ttymon -g -h -p huey console login: -T sun -d /dev/console -l console -m ldterm,ttcompat
As "expr" does not help in float computations, here are some of the alternatives
Using bc
$ Number=`echo 80 \* 10.69 bc`; echo $Number
Using AWK
$ Number=`(echo awk '{ print 80*10.69}')`; echo $Number
855.2
And if you are supplying something from outside
$ S=80; Number=`echo awk '{ print "'"$S"'"*10.69}'` ; echo $Number
855.2
One more way of passing variables to AWK
$ S=80; Number=`echo awk -v K=$S '{ print K*10.69}'` ; echo $Number
855.2
Digest for today morning: use of ${!VARIABLE} , learnt from Navojit Dutta(UNIX forum)
$ cat content
MYVAR=$1
DUMMY1="This is tricky"
DUMMY2=24
echo ${!MYVAR}
$ ./content DUMMY1
This is tricky
$ ./content DUMMY2
24
Problem : Replace the 100's in 3rd field with different numbers.
Sample file
jsaikia@KORA:~/prac$ cat fl
12|13|100|s
12|13|100|s
100|13|100|s
12|13|100|s
Output:
jsaikia@KORA:~/prac$ awk 'BEGIN{OFS=FS="|"}$3==100{$3=NR*131}{pri
12|13|131|s
12|13|262|s
100|13|393|s
12|13|524|s
jaduks@LOLA:~/prac$ cat fil2
090607:The rest of the line
091207:Also some text
091207:Here's some more text
[Output required]
06-09-07:The rest of the line
12-09-07:Also some text
12-09-07:Here's some more text
SED solution:
jaduks@LOLA:~/prac$ sed -e "s/^\(..\)\(..\)\(.*\)/\2-\1-\3/g" fil2
PERL solution:
#!/usr/bin/perl
open(FILE, "$ARGV[0]");
while(<FILE>){
$_=~s/^(\d{2})(\d{2})(\d{2})/$2-$1-$3/;
print $_;
}
I was struggling to use putty to access my localhost having cygwin installed on it.
This link helped me to configure Mark Edgar’s "Puttycyg" which allows one to create a virtual terminal for a cygwin environment using Putty instead of the command prompt offers.
http://craigtatham.com/wiki/Puttycyg
A nice one!!

