Tuesday, June 2, 2009

4 - Other techniques

===============================================================================

4 - Other techniques.

===============================================================================



Now let's move on to a little bit more advanced shell scripting.

Actually it's not that advanced, just more hard to keep in order, but let us

leave that to the head of the beholder..... errr

Anyway, let's not make this harder then it is, so here we go, with a script

example:








-------------------------------------------------------------------------------

#!/bin/bash



> file1.c



cat >> file1.c << EOF

#include

int main ( void )

{

printf("Hello worldn");

return 0;

}

EOF



cc file1.c -o file1 || gcc -o file1 file1.c

./file1



rm -f file1.c file1



exit 0



-------------------------------------------------------------------------------



And here as follows, is an semi-english translation of the script:



echo nothing to file1.c to create it.



cat to file1.c what comes here after in between the "EOF"'s

// --- a short hello world program in C code --- //



try if there is a 'cc', if not then use 'gcc'

Execute the newly compiled file



remove file1.c and file1



exit the script.



-------------------------------------------------------------------------------



This can be very useful, since bash do have it's limitations, so if you

ever need something more powerful or just something else, you can always

do like the script told.



Another little trick with the same thing in a script is:



more << EOF

Here you can type whatever,

like an agreement text or something.

EOF



Play around with it.



-------------------------------------------------------------------------------



Here let's have a look at the "case" command, case is like "if" ended

with it self backwards.

So that what starts with "case" ends with "esac", here's an example of "case":



-------------------------------------------------------------------------------



#!/bin/bash



case "$1" in

foo)

echo "foo was written"

;;

bar)

echo "bar was written"

;;

something-else)

echo "something-else was written"

;;

esac



-------------------------------------------------------------------------------



This is the same as saying:



....

if [ "$1" = "foo" ];then

echo "foo written"

fi

if [ "$1" = "bar" ];then

echo "bar was written"

fi



etc.

....



so case is far shorter if you have alot of arguments.



Here's a better example:



-------------------------------------------------------------------------------

#!/bin/bash



case "$1" in

--help)

echo "Usage: $0 [--help] [--port ] [--time]"

;;

--port)

telnet $3 $2

;;

--time)

date

;;

esac



-------------------------------------------------------------------------------



This is not very hard to learn,

case the first argument vector ($1) in

firt-possible-match)

if it matches do ........

close the ) with ;;

etc. down to "esac"



Really not much more to say about the case command at this point.



-------------------------------------------------------------------------------



Now let's have a REALLY quick look at the command `sed`, which is used

to format text. say now that you have a file called "tmp" that

contains the following:



http://www.metacrawler.com

http://www.yahoo.com

http://www.webcrawler.com



and you want to change all the "www"'s to "ftp", then you do like this:



sed 's/www/ftp/g' tmp



and if you want to store the changes to a file you can do:



sed 's/www/ftp/g' tmp > tmp2



This is not sed's only use, but for sure it's what it's most used for.



Here's just one other really simple thing sed could be used as:



sed -n 3 p /etc/passwd



This will print out the 3'd line of the /etc/passwd file.



-------------------------------------------------------------------------------



Now let's take up a really interesting command `dialog`, that is a command

with which you can create ncurses dialog boxes.

Ncurses dialog boxes are what one would call 'console graphics' or

'ascii color graphics', if you ever seen a blue background and a gray box

asking questions, with an and button, while running

something in a console you have seen an ncurses dialog box.



Now here is a small script example of a dialog box:



-------------------------------------------------------------------------------



#!/bin/bash



dialog --backtitle "My first dialog"

--title "Main menu"

--menu "Make your choice" 13 60 6

1 "First option"

2 "Second option"

3 "Exit" 2> .tempfile

output=`cat .tempfile`

rm -f .tempfile



if [ "$output" = "1" ]; then

dialog --msgbox "First option was entered" 5 40

fi



if [ "$output" = "2" ]; then

dialog --msgbox "Second option was entered" 5 40

fi



exit 0



-------------------------------------------------------------------------------



Here is another very small example with dialog boxes:



-------------------------------------------------------------------------------



#!/bin/bash



dialog --yesno "Do you agree, jada jada" 10 50 &&

dialog --yesno "ok you agreed" 10 50 ||

dialog --yesno "ok fine, leav then ..." 10 50



-------------------------------------------------------------------------------



If the first one (Do you agree, jada jada) returns 'true' (yes)

then it walks on to the next (ok you agreed),

and if any of those first two returns 'false' (no) it will display

the last (ok fine, leav then ...).



-------------------------------------------------------------------------------



Notes: The back slashes "\" are used to say "no new line" as in what comes

on the next line will be treated as if it were on the same line as

the last line, the "\" really means that the next character's

(in this case the new lines) special meaning is overlocked.



Just in case you didnt understand, the numbers after, like 10 50:

dialog --yesno "ok fine, leav then ..." 10 50

is the geometry of the window.

First number is height and the second width.



Another note being that the command `Xdialog` works the same as

dialog, but I wont take that up here because it doesn't come as default

with any other Linux distribution then Mandrake, as far as I know.



A final note is that the `dialog` command is getting to be out dated

but is still the most used, the newer version of it is named `whiptail`

and works the same as `dialog`, but looks slightly different.



-------------------------------------------------------------------------------



Now we have covered most of it, so let's take up some small tricks, that bash

allows you to do, here follows what it does, and then the code example:



Here we wanna check if you have a directory called "/usr/local/bin":



if [ -d /usr/local/bin ]; then

cp file /usr/local/bin/

else

echo "NO !!"

fi



Another way of doing the same thing is this:



test -d /usr/local/bin && cp file /usr/local/bin/ || echo "NO !!"



Or:



ls /usr/local/bin/ && cp file /usr/local/bin/ || echo "NO !!"



The last way is a bit messy, but alot smaller then the first one,

but here's yet another way that's small and less messy:



ls /usr/local/bin/ 1>/dev/null 2>&1 && cp file /usr/local/bin/ || echo "NO !!"



That might look really weird at first sight, but it's easy if you break it

down and look at it:



ls /usr/local/bin/ <<==== lists /usr/local/bin/

1>/dev/null <<==== sends the contents of the listing to 'the black hole'

2>&1 <<==== sends any errors the same way... to 'the black hole'

(same thing as to say 2>/dev/null)



&& <<==== if the first command worked, we will go on here.

cp file /usr/local/bin/ <<==== copy file to /usr/local/bin/

|| <<==== if the first command didn't work...

we go on here instead.

echo "NO !!" <<==== what it says ... say NO !!



as this:

If `ls` can list /usr/local/bin/ next command can be executed, OR if not

it will echo "NO !!", and all listings/errors are being sent to /dev/null

the 'black hole' of a UNIX/Linux.



-------------------------------------------------------------------------------



To prevent that a script is being executed more the once at the same time

for some reason you may wanna let the script make a 'lock' file.

This is very easy to do:



#!/bin/bash

ls script.lock 1>/dev/null 2>&1 && exit 0 && echo "lockfile detected"

> script.lock

echo "Here is where the script should be"

rm -f script.lock

exit 0



Here we first check if there is a lockfile, and if there is we terminate

the script and say that a lockfile was detected.

If there is no lockfile, we create one and start to execute the rest of the

script.

At the end of the script we remove the lockfile, so that the script can

be executed again.

All this is just to prevent the same script to be run twice at the same time,

which can be a good thing if your script does something that cant be done

twice at the same time, as mounting a hard drive/cd-rom, using sound or

anything like that.



Another neat little trick is if you from within a script are going to

create temporary files that you want unique (to not overwrite some other

files anywhere, wherever the script may get executed) you can do like this:



#!/bin/bash

echo "ls" >.tmp.$$

echo "-la" >.tmp2.$$

one=`cat .tmp.$$`

two=`cat .tmp2.$$`

$one $two

rm -f .tmp.$$ .tmp2.$$



This will make a file called .tmp. containing the word "ls",

then it will make a file called .tmp2. containing "-la".

After that it make 2 variables, each one when being called will `cat` one

of the .tmp.* files each.

At the end we have "$one $two" that will work the same as if we had printed:



ls -la



And last we remove the temporary files.



This is useful if your doing a script that requires you to move around alot

of text from one file to another and back, as this example:



#!/bin/bash



sed 's/www/ftp/g' tmp > .tmp.$$

sed 's/com/org/g' .tmp.$$ > .tmp2.$$

sed 's/ /_/g' .tmp2.$$ > .tmp3.$$

mv -f .tmp3.$$ tmp

rm -f .tmp.$$ .tmp2.$$ .tmp3.$$



exit 0



Here we change all www's in a file (tmp) to ftp, then we change all com's

to org, and then all spaces to underscores.

After that we move the fully changed file so it overwrites the original file.

Then removing the temporary files and exit the script.



If you have a good look at it, it's really easy.



Another nice trick is as I showed in the example prior to the last one:



...

one=`cat .tmp.$$`

two=`cat .tmp2.$$`

...



That a variable can hold a command can prove to be useful, like this:



#!/bin/bash

time=`date +%H:%M:%S`



echo "$time" >> log

echo "some input to the log" >> log

sleep 60

echo "$time" >> log

echo "some input to the log a minute later" >> log



exit 0



But, it can hold more then just a command, it can actually *hold* the contents

of a whole file.

Say now that you made a script and have a pretty large readme file, and want

to display that as a 'man page' to the script if the argument `--help`

is used to execute the script, then you can do like this:



#!/bin/bash

one="$1"

help=`cat README`



if [ "$one" = "--help" ]; then

$help | more

...



Ofcorce it would be easier to say:



#!/bin/bash

if [ "$?" = "--help" ]; then

more README

fi



But these examples are just here for illustration so you get the point

of usage for commands and so.



Another trick is, if you wanna hide script/program you can rename it to:

-bash, that way it will look as a normal bash running in the `ps`

(process list), you rename it by doing:



mv script ./-bash



Then execute it like normal ./-bash



Yet another trick, is if you're doing a script where you want each line

of a file as a variable, unlike for that takes each word as a variable.

This can be done like this:



#!/bin/bash

file="$1"

min="0"

max=`cat $file | wc -l`



if [ "$1" = "" ]; then

echo "Usage: $0 "

exit -1

fi



while [ "$min" != "$max" ]; do min=`expr $min + 1`

curline=`head -$min $file | tail -1`

echo $curline

test $min -eq $max && exit 0

done



The `test` is there to make sure that it will end when $min and $max are the

same.

Now this can be done with `for` if you change IFS (described later), but that

is not recomended, especially if you export IFS since that would change the

enviorment and hence screw with the system scripts if they were to be runned

before changing IFS back to normal, but enough about that now, just keep

it somewhere far back in your head, dont change IFS unless you know what

you're doing.



If you dont understand this little script at this point, dont worry, you

will understand it the second time you read this tutorial =)



-------------------------------------------------------------------------------



Now let's take a quick look at arrays in shell scripting.

First off, an array is what it says, it's an array of something,

now, to declare a variable that can hold an array we create it with the

command `declare`, let's make a short example:



alien:~$ declare -a foo=(1 2 3 4 5)

alien:~$ echo ${foo[0]}

1

alien:~$ echo ${foo[1]}

2

alien:~$ foo[1]=bar

alien:~$ echo ${foo[1]}

bar

alien:~$



First of all, to understand the declare command better do `help declare`

at a console and it'll desplay this:



declare: declare [-afFrxi] [-p] name[=value] ...

Declare variables and/or give them attributes. If no NAMEs are

given, then display the values of variables instead. The -p option

will display the attributes and values of each NAME.



The flags are:



-a to make NAMEs arrays (if supported)

-f to select from among function names only

-F to display function names without definitions

-r to make NAMEs readonly

-x to make NAMEs export

-i to make NAMEs have the `integer' attribute set



Variables with the integer attribute have arithmetic evaluation (see

`let') done when the variable is assigned to.



When displaying values of variables, -f displays a function's name

and definition. The -F option restricts the display to function

name only.



Using `+' instead of `-' turns off the given attribute instead. When

used in a function, makes NAMEs local, as with the `local' command.



So here we see that the -a switch to declare makes the variable an array.

So after getting that `declare -a` we declare the variable as an array,

with the array within paranteses.



And then to make use of it, we use the way to write a variable like this:

${ variable name here }

and the number inside the []'s is the number that points to which part of

the array it should use, begining from 0 which is the first.



Let's make another short example:

declare -a foo=(this is another example)

echo "The array (${foo[*]}) has (${foo[0]}) as first, and (${foo[3]}) as last."



The output of this would be:

The array (this is another example) has (this) as first, and (example) as last.



Now, this isn't something you'll use in every day scripting, but it's still

something you should know the existance of, just in case you see it or need

it at some point.



-------------------------------------------------------------------------------



Now here's a less common way of using bash, CGI scripts.

Most people dont assosiate shell scripting with cgi, but it works

just as well as any other language, so here I'd like to to show

you how to make CGI scripts in bash.



-------------------------------------------------------------------------------



Here is the first example which is a simple cgi counter in bash.

A note is that all CGI scripts should be in the servers cgi-bin directory

or any subdirectory there off, unless the server is configured to see

any other directorys as cgi directorys.



-------------------------------------------------------------------------------



#!/bin/bash



test -f date.txt || echo `date "+%B %d %Y"` > date.txt

test -f counter.txt || echo '0' > counter.txt

current=`cat counter.txt`

date=`cat date.txt`

visitor=`expr $current + 1`



echo "$visitor" > counter.txt

echo 'Content-type: text/html'

echo ''

echo '
Vitor:'

echo '
'$visitor'
Since'

echo '
'$date'
'



-------------------------------------------------------------------------------



Let's take this one line by line here:



First the shell ....

#!/bin/bash



Then we test if there is a file called date.txt, if not then we echo the

current date to it and hence creating it.

test -f date.txt || echo `date "+%B %d %Y"` > date.txt



Then we test if there is a file called counter.txt and if not we echo a 0

to it and so create that one too.

test -f counter.txt || echo '0' > counter.txt



Now we declare the variables, current is the contents of counter.txt.

current=`cat counter.txt`



The date variable is the contents of date.txt.

date=`cat date.txt`



And visitor is the sum of the contents of counter.txt + 1.

visitor=`expr $current + 1`



And then we echo the new increased number to counter.txt.

echo "$visitor" > counter.txt



And here comes the HTML part. the first to line is the 'cgi header'

those should ALWAYS be there:

echo 'Content-type: text/html'

echo ''



Then we move on to the *real* html:

echo '
Vitor:'

echo '
'$visitor'
Since'

echo '
'$date'
'



The
is a linebreak in html

The bash variables have to be *ouside* the 's else they will simply show

up as $visitor or $date litterly, that's why it's made like this:

echo 'text' $variable 'some more text'

So that the text is enclosed with 's, but the variables are between or rather

outside of them.

Anyway, this cgi will create a section that looks like this on a webpage:



----------------

Vitor:

1

Since

May 29 2001

----------------



To add that to a html code you add this tag to your html/shtml page:







With the path to the counter it could look like this:







Not so hard is it ?



-------------------------------------------------------------------------------



Here is another example of a CGI script in bash (actually the second CGI

script I ever made).



-------------------------------------------------------------------------------



#!/bin/bash



method=`echo $QUERY_STRING | awk -F'=' '{print $1}'`

host=`echo $QUERY_STRING | awk -F'=' '{print $2}'`



if [ "$method" = "nslookup" ]; then

echo 'Content-type: text/html'

echo ''

echo ''

echo ''

echo '
'

echo '
nslookup '$host' (This might take a second)
'

echo '
'

echo '
'

echo '
'

nslookup $host

echo '
'

echo '
'

echo '
'

echo '
nslookup compleat'

echo '
'

echo ''

echo ''

fi



if [ "$method" = "ping" ]; then

echo 'Content-type: text/html'

echo ''

echo ''

echo ''

echo '
'

echo '
ping '$host' (This might take a second)
'

echo '
'

echo '
'

echo '
'

ping -c 5 $host

echo '
'

echo '
'

echo '
'

echo '
ping compleat'

echo '
'

echo ''

echo ''

fi



if [ "$method" = "scan" ]; then

echo 'Content-type: text/html'

echo ''

echo ''

echo ''

echo '
'

echo '
Scanning host '$host' (This might take a minute)
'

echo '
'

echo '
'

echo '
'

nmap $host

echo '
'

echo '
'

echo '
'

echo '
Scan compleat'

echo '
'

echo ''

echo ''

fi



-------------------------------------------------------------------------------



Now let's take a look at what that means:



-------------------------------------------------------------------------------



This time it wont be all the lines, but all the new parts:



First the 2 variables:

method=`echo $QUERY_STRING | awk -F'=' '{print $1}'`

host=`echo $QUERY_STRING | awk -F'=' '{print $2}'`



These are made this way because of how the CGI script imports the variables

from a form (I'll come back to this), the $QUERY_STRING variable is

from the webservers enviorment, and so is one of the httpds env variables.



And what you do with the $QUERY_STRING is depending on how you create

your web form .... but as I said I'll get back to that.



Now the rest:



if [ "$method" = "nslookup" ]; then



That was pretty obvious .... if the first feild of $QUERY_STRING (separated

by a =, is "nslookup", then go ahead here:



echo 'Content-type: text/html'

echo ''



Yes the header ....



echo ''

echo ''

echo '
'

echo '
nslookup '$host' (This might take a second)
'

echo '
'

echo '
'

echo '
'



Create a HTML page ... and then after the
 we do the actual center part

of the script:



nslookup $host



Which will resolve the DNS of the host (try the command and see),

And after that we end the html page:



echo '
'

echo '
'

echo '
'

echo '
nslookup compleat'

echo '
'

echo ''

echo ''



and then end the if statement:



fi



and then the same for the others, just diffent objects at what they should

do, as this was nslookup, the other sections will mnap (portscan) and ping

the host instead.



Now how would a full HTML page look to make use of this cgi script ?

As we this time need input to get the host or IP to scan/ping/nmap.



Well like this:



-------------------------------------------------------------------------------









Enter host or IP

















































-------------------------------------------------------------------------------



Now what does all this mean ? ....

Well, I wont turn this into a HTML tutorial, but I'll explain this so you

can make use of bash for CGI.



Right to the important HTML part here:















Here we create a form, as in an input feild, which will add it's input

(in a specific way) to the end of the url in action="".

the method is get since we're getting the output of the cgi script.

we name this feild scan so we get the output this way:

?scan=

Where the is what you typed in the input box.

And then we make an "ok" button that says "portscan".

So if you type say 127.0.0.1 and press the portscan button the URL it will

be directed to is this:



http://www.yourdomain.com/cgi-bin/scan.cgi?scan=127.0.0.1



And this "scan=127.0.0.1" will be the $QUERY_STRING enviormental variable.



And so the script is starting to make sense.



-------------------------------------------------------------------------------



Here's a REALLY simple cgi script just for the illustration aswell.



-------------------------------------------------------------------------------



#!/bin/bash



string="Hello World"



echo 'Content-type: text/html'

echo ''

echo ''

echo '
'$string'
'

echo ''



-------------------------------------------------------------------------------



And the html to call that ..... just a normal hyper link.



Link



And that's it.



-------------------------------------------------------------------------------



That's it on the tricks, now let's move on to practical examples so you get

a little bit of feel for how you can use bash to make things easier for you.

No comments:

Post a Comment