You are viewing jaduks

I am here

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

This is funny. I was doing some tar's of directories. The tar happened, but due to my mistake the tar name became -C . I was trying to move/delete the same, tried single quote/double quote/escape.. Nothing worked.Came to know about the solution from one of my colleague.

 Since the file name begins with -, commands like mv, rm treats the file name itself as a parameter(option) to the command. To nullify this affect, we have to put -- before the file name.

rm -- -C

Setting default value for a BASH variable

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,
                                                          #If 1st arg is not entered, set it to DEFAULTVALUE

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

"dialog"  utility is superb for developing "small" front-ends.  I was aware of it , but never tried it.  Some days ago I came up with some small applications using it, those looks pretty good(as per my friends views). Here I will discuss about how to program with "dialog" in BASH.
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

Find all files between two dates

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.

Pushing data to Mysql using BASH

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.

Transpose of a matrix : BASH way of doing

A one liner for generating of a transpose of a matrix.


$ 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}} END {for(i in arr) print arr[i]}' | sed 's/^\t//'
1       4       7
2       5       8
3       6       9

Colors in BASH

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

A CHESS board

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`
                                        if [ $S -eq 0 ]; then
                                                echo -e -n "\033[47m " ### white ###
                                        else
                                                echo -e -n "\033[40m " ### black ###
                                        fi
                        done
                echo -e -n "\033[40m" #### background colour=black ###
                echo "" #### printing new line ###
        done

 

Add/Change/Insert lines using sed


 Using sed we can  Add/Change/Insert lines in a file, I found it very useful, hope you too !

$ cat namedb.txt
Nina:London
Apen:India
Lokesh:India

#Add a line
a)
$ sed '
/Apen/ a\
Add this line after Apen
' namedb.txt

<Output>
Nina:London
Apen:India
Add this line after Apen
Lokesh:India

b)
$ sed '
2 a\
Add this line after 2nd line
' namedb.txt

<Output>
Nina:London
Apen:India
Add this line after 2nd line
Lokesh:India

#Insert a new line before
c)
$ sed '
/Apen/ i\
Insert this line after Apen
' namedb.txt

<Output>
Nina:London
Insert this line after Apen
Apen:India
Lokesh:India

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:London
Change the line with Apen to this line
Lokesh:India

Similarly one can mention the line number to change (case b above)

 

Writing log functions in BASH scripting

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 ..

Pause function in shell script



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"

 

Command: seq - print a sequence of numbers

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++))
  

Protecting a shell script!!


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

Same Task, different ways of doing!

 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 
 

Reading passwords in shell script

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
 

Adding Salary: AWK

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 

 

Report Generation using AWK



"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

Sundays in a year ?

#!/bin/sh

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/

Simulating "show" command

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.

 

Take a .bak of all my .txt files, please.

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

OPening man page in vim (linux)

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

Move all *.txt.bak files to *.txt

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

Renaming all .html files to .doc

 [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

Split PATH: One per Line

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/home/jsaikia/impscripts/

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

float computation

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

Content of content of a variable !

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}{print}' fl
12|13|131|s
12|13|262|s
100|13|393|s
12|13|524|s

Need formatting in output

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!!

Profile

jaduks
Jadu Kumar Saikia

Latest Month

January 2008
S M T W T F S
  12345
6789101112
13141516171819
20212223242526
2728293031