=========================== Aliens Bash Tutorial ============================== --------------- Written by Billy Wideling <-> alien@koping.net ---------------- ----------------- Publicato su http://jack.logicalsystems.it ------------------ =============================================================================== First you probably need to read a UNIX command bible to really understand this tutorial, but I will try to make it as clear as possible, there is about 100-150 UNIX commands explained later in this tutorial. You are to have some UNIX experance before starting on this tutorial, so if you feel that you have UNIX/Linux experance feel free to start to learn here. What I included here is general shell scripting, most common other things and some UNIX commands. Here's the most common shell types: bash = Bourne again shell sh = shell csh = C shell tcsh = Tenex C shell (not tab-completion-extended C shell) tclsh = Tcl shell ksh = korn shell ash = a shell bsh = bourne shell ? (in most Linux distributions it's a link to /bin/ash) zsh = the Z shell (it's what it's manual page tells about it .. :/ ) =============================================================================== 1 - What you already know. (should know) =============================================================================== Here we go, bash scripting is nothing more then combining lots of UNIX commands to do things for you, you can even make simple games in bash (just UNIX commands) or as in normal cases, batch files to control things in your computer. There is a variety of easyer and harder examples in the beginning of this tutorial, I've done it this way to make it easier for people to get the general picture, so they will get more of the "aha!" experiences in the later chapters of this tutorial. What bash or any scripting language does is to call for premade programs that lives in your computer. So when you write a command in a script you are calling a command that is a part of the system. That is why this tutorial will be just as much a Linux and UNIX tutorial as a shell scripting tutorial. I will however not take up much about The X Windows System in this tutorial, for the simple reason that a Window Manager does nothing else then display programs. This means that a Window Manager is like a graphical shell for the system. You can do all in this tutorial from any terminal emulator in a Linux Graphical Enviorment (The X Windows System). A terminal emulator would be such as: Eterm, xterm, axvt, rxvt, kterm etc. A terminal emulator let's you get up a terminal with a command prompt in a graphical window. ------------------------------------------------------------------------------- Shell command separator/control characters: | = pipe will take the first commands stdout as the second commands stdin. || = OR if first command is false, it will take the second. |= = OR IS (mostly used in if statements) && = AND if first command is true, it will execute the second one. ! = NOT (mostly used in if and test statements), but as a shell-command it opens a shell to run the command (ex. `! echo foo`) != = NOT IS (mostly used in if statements) !$ = last commands last argument !! = repeat last command = = IS (mostly used in if statements) ; = will separate 2 commands as if they were written on separate command lines ;; = end of a case function in a case statement. (see `case` further down) $ = prefix to a variable like "$myvar" $! = PID of the last child process. $$ = PID of current process (PID == Process ID) $0 = Shows program that owns the current process. $1 = First agument supplied after the program/function on execution. $2 = Second agument supplied after the program/function on execution. ($3 etc.) $# = Shows the number of arguments. $? = Any argument (good to use in `if` statements) $- = current option flags (I never ever had to use this one) $_ = Last argument/Command $* = All arguments $@ = All arguments # = remmed line, anything on a line after "#" will be overlooked by the script { = start braces (starts a function) } = end braces (ends a function) [ = start bracket (multiple-argument specifiers) ] = end bracket (multiple-argument specifiers) @ = $@ is equivalent to "$1" "$2" etc. (all arguments) * = wild card (* can substitute any number of characters) ? = wild card (? can substitute any single character) " = quote ' = precise quote. (Will even include "'s in the quote) ` = command quote. (variable=`ls -la` doing $variable will show the dir list) . = dot will read and execute commands from a file, ( . .bashrc ) & = and. as suffix to executed file makes it go to the background(./program &) 0> = stdin stream director (I never seen this used in any script) 1> = stdout stream director (standard output) 2> = stderr stream director (standard error output) % = job character, %1 = fg job 1, %2 = fg job 2, etc. >> = stream director append to a file << = stdin stream director. (cat > file << EOF ; anything ; EOF) > = stream director that will start at the top of the file (in if statements < and > may be used as greater-then and lesser-then, as: if [ "$1" >= "2" ]) \ = back-slash, takes away any special meaning with a character, \$var will not be treated as a variable. (and a new line will not be treated as a new line) Also a \ before a command, removes any alias on the command as: \rm >& = stream director to stream director, ie. echo "a" 1>/dev/null 2>&1 this directs 2> to the same place as 1> ------------------------------------------------------------------------------- Here is the basic UNIX or rather Linux directory structur: / (system root) _______________________________|____________________________________ | | | | | | | | | | | | bin/ boot/ dev/ etc/ home/ lib/ lost+found/ proc/ root/ sbin/ usr/ var/ | | | | | | | | | | | | | | | | | | | | | | | |-> various | | | | | | | | | | | | | | | | | | | | | |-> Read later.. | | | | | | | | | | | | | | | | | | | |-> Superuser Binarys | | | | | | | | | | | | | | | | | |-> User roots home dir | | | | | | | | | | | | | | | |-> system info etc. | | | | | | | | | | | | | |-> Orphan files (look here after crashes) | | | | | | | | | | | |-> System / programming librarys | | | | | | | | | |-> Contains the user accounts home directorys | | | | | | | |-> System configuration files. | | | | | |-> Devices | | | |-> The kernel and kernel maps. | |-> Executeble files. (binarys) The /usr directory contains a whole lot of things, mainly user accesseble things, like binarys in /usr/local/bin/ and /usr/bin/ also librarys in /usr/lib/ and /usr/local/lib/. The kenrel source should also be under /usr, in /usr/src/linux/ But more about that later. ------------------------------------------------------------------------------- Here's an example of the following files locations: /dev/null /dev/fd0 /etc/passwd /home/alien/.profile /usr/local/bin/BitchX / _______________________________|____________________________________ | | | | | | | | | | | | bin/ boot/ dev/ etc/ home/ lib/ lost+found/ proc/ root/ sbin/ usr/ var/ | | | | | | alien/ local/ | | | | | | |-> .profile | | |-> passwd bin/ | | |-> null |-> BitchX |-> fd0 ------------------------------------------------------------------------------- And a final example that's a bit more stripped, just incase you are really new to computers. Here following files locations: /dev/null /dev/audio /dev/hda1 /etc/passwd /etc/shadow /home/alien/.profile /home/alien/tmp/somefile /home/user/.bash_profile /usr/local/bin/BitchX /sbin/shutdown / _____________________________|_____________________________ | | | | | dev/ etc/ home/----- usr/ sbin/ | | | | | | |-> null | | | local/ |-> shutdown |-> audio | | | | |-> hda1 | | | bin/ |-> passwd alien/ | | |-> shadow | user/ |-> BitchX | | | |-> .bash_profile | |-> .profile | tmp/ | |-> somefile ------------------------------------------------------------------------------- This is the same structure as on any Operating System that uses directorys, Though some Operating Systems may call the / directory C: and some other may call it HD etc. and ofcorse some of the directory names in Linux/UNIX are UNIX specific. No further explanation should be nessesary. After reading this tutorial, brows around the system and try to learn what all the files does, just dont remove any files you didnt put there until your ABSOLUTLEY sure of what you are doing. ------------------------------------------------------------------------------- Here's a few UNIX commands just for illustration: echo ====== echo will *echo* anything you add to it like this: alien:~$ echo "blah" blah alien:~$ To get it to echo without a new line add the suffix -n like this: alien:~$ echo -n "blah " blah alien:~$ I'll get back to why you want to do "-n" sometimes in a while. read ====== read will read from the keyboard (stdin) and save it as a variable the variable name goes after the read command, like this: alien:~$ read myvar alien:~$ echo $myvar blah alien:~$ To combine these two commands (echo and read) in a small script line, it can look like this: alien:~$ echo -n "password: " ; read pass ; echo "Your pass is $pass" password: Your pass is mypass alien:~$ Get the basic idea ? ------------------------------------------------------------------------------- Anyway, here are some commands that you should know before moving on with this tutorial - [*] after == important to know. [X] after == very basics will do. ls [*] Ex: ls -la Long directory listing. echo [*] Ex: echo "foo" Does what it says. cat [*] Ex: cat /etc/passwd Dump out the content of a file. less [X] Ex: less /etc/passwd Scroll up and down in a file (q = exit) head [X] Ex: head -5 /etc/passwd Get the 5 (-5) first lines of a file. tail [X] Ex: tail -7 /etc/passwd Get the 7 (-7) last lines of a file. grep [*] Ex: grep x /etc/issue Dump lines containing x from /etc/issue chmod [*] Ex: chmod a+x file Give everyone executable rights to file chown [X] Ex: chown root file Change owner of file to root. (cd [-] Ex: cd /etc Change Directory to /etc) Some applications you need to know how to operate (basics will do) : any text editor (preferably emacs or vi - they are explaind last in this file) telnet Ex: telnet 127.0.0.1 Opens a connection to IP 127.0.0.1 lynx Ex: lynx http://foo.bar A command line based web browser. ftp (ncftp) Ex: ncftp ftp://foo.bar A command line based ftp client. ssh Ex: ssh 127.0.0.1 Opens a secure connection to 127.0.0.1 These are all explained fully later in this tutorial. ------------------------------------------------------------------------------- Now don't sit there and ask yourself how's going to teach you the commands or applications I just listed here above. use the manual pages. like this: man echo that will get you the full manual on the command echo :) man works the same way with applications that you have the manual pages for. To get out of the manual page just press the letter "q". "q" quits it and bet's you back to the command line prompt. man uses all the normal "less" commands. Or read further down in this tutorial in the basic Linux/UNIX commands and operations section (8). ------------------------------------------------------------------------------- The key to shell scripting just as with any programming language/Operating System is to REALLY understand what you are doing, so do read this file more then once, and don't read it to fast. Take your time and let it sink in, so you know what it's all about, and do take time to read manual pages and do some playing with the commands so you learn them. Now that should be enough of what you *should* know before starting to learn UNIX shell scripting. So here we go....... =============================================================================== 2 - Where to start =============================================================================== You should always start with very simple scripts that you really don't have any practical use for but still *could* be of practical use =) As for first let's make what we already know to a *real* executable script. Open a text editor (name the file myscript.sh) and type this: #!/bin/bash echo -n "password: " read pass echo "Your pass is $pass" save & exit --- then do this command: chmod u+x myscript.sh Then we can execute it: alien:~$ ./myscript.sh password: Your pass is alien:~$ The "#!/bin/bash" at the start of the file is to let the script know what shell type it should use. The "chmod u+x myscript.sh" is to make it executable. (change-mode user+execute-right-on myscript.sh) ..... read the manual pages on chmod for more info on it =) ------------------------------------------------------------------------------- Take alot of time to play around in your system, open files, figure out what they do (but don't edit or remove them). Take time also to learn some good text editor, that's important. Learn, emacs or vi, those are by most people considerd the absolutly best, but jed, joe, elvis, pico or any simple editor like that will do just fine for now. emacs and vi are explained later in this tutorial. ------------------------------------------------------------------------------- Another thing before moving on is that you can export a variable from a script. Say that you have the variable "$var" in a script and want to export it to the system for use with some other script or something, you can do: export var Like this little script: ------------------------------------------------------------------------------- #!/bin/bash VAR="10" export VAR ------------------------------------------------------------------------------- Note: VAR="10" can not be written as VAR = "10", since it's 'whitespace-sensetive'. ------------------------------------------------------------------------------- But more to how to make scripts in a second, I just thought that this would be a good time to enlighten you about this. So here we go .... =============================================================================== 3 - Beginning techniques. =============================================================================== First off I'm going to show how to count in bash or how to use your command line as a calculator, which is really easy and useful. alien:~$ echo $[ 4 * 2 ] 8 alien:~$ or another example: alien:~$ echo $[ 10 + 5 ] 15 alien:~$ Easy ? .... I think it is =) The internal calculator can also be used like this: alien:~$ echo $(( 10 + 5 )) 15 alien:~$ The second way of using the internal shell calculator is here just so you dont get confused if you see it used that way sometime. ------------------------------------------------------------------------------- Now I'd like to show the *string comparing* with "if" statements, which can be a little hard at first contact, but as soon as you get familiar with it, it wont be any problems. So here's an example. ------------------------------------------------------------------------------- #!/bin/bash echo -n "enter a name: " read var1 echo -n "enter another name: " read var2 if [ "$var1" = "$var2" ]; then echo "The names are the same" else echo "The names were not the same" fi exit 0 ------------------------------------------------------------------------------- Note: "fi" is the ending of if, just like a "}" is the ending of a "{", exit 0 terminates the script correctly and returns you to the prompt. Another note is that instead of '=' you can use '-eq' to test if 2 expressions are equal, or '-eg' to check if 2 integers are equal, etc. It should also be said that a variable say '$var' can be written as this: ${var}, just so you know if you see it done that way in some scripts, but here we will use the $var way. ------------------------------------------------------------------------------- This example if executed looks like this: (Matching names) alien:~$ ./script1.sh enter a name: smurf enter another name: smurf The names are the same alien:~$ (Non-matching names) alien:~$ ./script1.sh enter a name: smurf enter another name: guru The names were not the same alien:~$ You can compare any 2 strings with this, as this *mid script example*: ... if [ "$user" = "gnu" ]; then echo "Hello user gnu !" else echo "I don't know you." fi ... This compares a variable with a static string which you can set to anything. You can also do this the other way around. ... if [ "$user" != "gnu" ]; then echo "I don't know you." else echo "Hello user gnu !" fi ... The '!=' means NOT-IS, in clear text if the 2 strings are not a match. As the above example in straight English: ... if the variable don't match the word gnu, then say "I don't know you." in other cases say "Hello user gnu !" ... If you think that a variable may not contain anything and you wanna avoid it showing you errors you can add an x as the first character to both the statements to test with if, like this to compare $one with -x: ... if [ "x$one" = "x-x" ]; then echo "$one is -x" else echo "$one is not -x" fi ... In plain english: ... if (contents of $one) equals -x (supress error messages if any), then say (contents of $one) is -x in other cases say (contents of $one) is not -x ... This previous way is actually quite old, and only a precaution, say this: ... echo -n "enter a number: " read foo if [ $foo = 2 ]; then echo ok foo fi ... Now, if you with this example dont enter any number there will be nothing there for if to compare with, not even a blank "", since we're not using quotes, but as this: ... echo -n "enter a number: " read foo if [ x$foo = x2 ]; then echo ok foo fi ... There will always be something, because if $foo is nothing there is still x. Just read acouple of times and you'll get it. You can also test if a variable contains anyting at all like this: ... echo -n "enter a number: " read foo [ -n $foo ] && echo ok foo ... This uses the same options as the test command, so "-z" will return true if the variable is empty, "-z" will return true if the variable is not empty etc, it's ok if you dont understand this right now .... I've added this for the second time readers. You can also test if a command turns out as true, like this: ... if echo "foo" >/dev/null; then echo "foo" else echo "bar" fi ... Here if will check if foo echos to /dev/null, and if so, then it will print out "foo" and if foo didnt echo to /dev/null, it'll print out the word "bar" Anoter and perhaps *cleaner* way of doing the same is this: ... if (echo "foo" >/dev/null); then echo "foo" else echo "bar" fi ... It's the exact same thing but with parenthases around the command, it looks much cleaner ... and so the code is easyer to follow. You can also make if think 'if this is a match or that is a match', like if the variable is one of two options do one thing else do another. Like this: ... if [ "$user" = "gnu" -o "$user" = "ung" ]; then echo "Hello $user !" else echo "I never heard of $user..." fi ... The '-o' means OR in an if statement, so here is the example in plain english: ... if the variable matches the word gnu or matches the word ung, then say Hello word ! (the word is the variable, now gnu or ung) in other cases say "I never heard of word... (the word is whatever the variable is set to) ... ------------------------------------------------------------------------------- Note: The quotes are needed in an if statement in case the strings or variables it's suppse to compare is empty, since if [ foo = ]; then would produse a syntax error, but if [ "foo" = "" ]; then would not. ------------------------------------------------------------------------------- The '-o' can also be made with '] || [', so that: if [ "$user" = "gnu" -o "$user" = "ung" ]; then can also be expressed as this: if [ "$user" = "gnu" ] || [ "$user" = "ung" ]; then You dont really need to remember that, but for the knowlidge I desided to make a note out of that anyway, mostly for the more experianced readers of this tutuorial, and for the readers that have read it several times. ------------------------------------------------------------------------------- You can also set static text in a variable, which is really easy: ------------------------------------------------------------------------------- #!/bin/bash anyvar="hello world" echo "$anyvar" exit 0 ------------------------------------------------------------------------------- Which executed would look like this: alien:~$ ./myscript hello world alien:~$ Easy enough ? =) ------------------------------------------------------------------------------- Now let's move on to "for" and common for-loops. I am actually only going to show one sort of for-loop example, of the reason that at this stage no more is needed, and would only confuse. as a note, for loops-can be uses (as soon shown) to import strings from a file to be used as variables in the script. Now, here's the example: ------------------------------------------------------------------------------- #!/bin/bash for VAR in `cat list.txt`; do echo "$VAR was found in list.txt" done exit 0 ------------------------------------------------------------------------------- Note: "done" terminates the loop when finished. "in" and "do" are like bash *grammar*, I'll explain that later. The `cat list.txt` part, the `s around the command will make sure the script/line executes that part as a command, another way of doing this is to: $(cat list.txt) which has the same effect. That's just a note so you wont get confused if you see it used that way some time. ------------------------------------------------------------------------------- The previous script example is dependent on that there is a file called "list.txt", so let's make such, and fill it with something like this: list.txt 123 234 345 456 567 678 789 890 ------------------------------------------------------------------------------- Then the executed script would look like this: alien:~$ ./script2.sh 123 was found in list.txt 234 was found in list.txt 345 was found in list.txt 456 was found in list.txt 567 was found in list.txt 678 was found in list.txt 789 was found in list.txt 890 was found in list.txt alien:~$ ------------------------------------------------------------------------------- Note: A space in a file read by a for-loop is taken the same way as a new line. ------------------------------------------------------------------------------- Here is another example, with a for-loop with an if statement: ------------------------------------------------------------------------------- #!/bin/bash for VAR3 in `cat list.txt`; do if [ "$VAR3" = "789" ]; then echo echo "Match was found ($VAR)" echo fi done exit 0 ------------------------------------------------------------------------------- And executed that looks like this: alien:~$ ./script3.sh Match was found (789) alien:~$ If you have read this in a calm fashion it should be quite clear to you so far, but before I move on to real practice examples I will explain the while-loop, and some, more which can be used as to count and more, for various purposes, as you will see. You don't have to *understand* all of how this works, but you should at least learn it. So here we go on an example with "while": ------------------------------------------------------------------------------- #!/bin/bash count="0" max="10" while [ $count != $max ]; do count=`expr $count + 1` echo "We are now at number: $count" done exit 0 ------------------------------------------------------------------------------- Note: expr is a calculator command, you can read more about it later in this tutorial. ------------------------------------------------------------------------------- This in plain English reads the following: make variable "count" hold the number 0 make variable "max" hold the number 10 while 0 is not 10, do add 1 to 0 (each loop until it is 10) say "We are now at number: $count" (each time 1 is added as long as we are in the loop) end the loop return to the prompt command line. ------------------------------------------------------------------------------- Which executed looks like, (you guessed it), this: alien:~$ ./count.sh We are now at number: 1 We are now at number: 2 We are now at number: 3 We are now at number: 4 We are now at number: 5 We are now at number: 6 We are now at number: 7 We are now at number: 8 We are now at number: 9 We are now at number: 10 alien:~$ ------------------------------------------------------------------------------- Here is another example of a while loop. #!/bin/bash agreement= while [ x$agreement = x ]; do echo echo -n "Do you agree with this ? [yes or no]: " read yesnoanswer case $yesnoanswer in y* | Y*) agreement=1 ;; n* | n*) echo "If you don't agree, you can't install this sofware"; echo exit 1 ;; esac done echo "agreed" echo ------------------------------------------------------------------------------- This in plain English reads the following: Make an unknown variable named agreement while the unknown variable is unknown and doesnt match the case, say "Do you agree with this ? [yes or no]: " read the the answer into the "yesnoanswer" variable. make a case and check the "yesnoanswer" variable for any words beginning with y or Y, and if so, skip the rest and go on with the script and say "agreed". if it doesnt begin with y or Y, check if it starts with n or N. If it does start with a n or N, then say: "If you don't agree, you can't install this sofware" and quit the script. ------------------------------------------------------------------------------- Which executed looks like this: alien:~$ ./agree.sh Do you agree with this ? [yes or no]: something Do you agree with this ? [yes or no]: yes agreed Again executed, but with 'no' as answer: alien:~$ ./agree.sh Do you agree with this ? [yes or no]: nothing If you don't agree, you can't install this sofware alien:~$ ------------------------------------------------------------------------------- Note that "nothing" begins with 'n' and therfor matches what the script is looking for, y or Y, and n or N. Also see later in the tutorial about `case` statements. ------------------------------------------------------------------------------- Now I'm going to explain shortly about functions in bash. A function is like a script within the script, or you could say that you make your own little command that can be used in a script. It's not as hard as it sounds though. So here we go on a example: ------------------------------------------------------------------------------- #!/bin/bash function myfunk { echo echo "hello" echo "this is my function" echo "which I will display twice" } myfunk myfunk exit 0 ------------------------------------------------------------------------------- Which executed looks like this: alien:~$ ./funk.sh hello this is my function which I will display twice hello this is my function which I will display twice alien:~$ ------------------------------------------------------------------------------- Another example of functions can look like this: ------------------------------------------------------------------------------- #!/bin/bash myvar="$1" var2="$2" if [ "$myvar" = "" ]; then echo "Usage: $0 " exit 0 fi function myfunk { expr $1 + $2 } myfunk $myvar $var2 exit 0 ------------------------------------------------------------------------------- Which executed looks like this: Without any arguments: alien:~$ ./funk.sh Usage: ./funk.sh With arguments: alien:~$ ./funk.sh 12 3 15 alien:~$ ------------------------------------------------------------------------------ Note: the $1 and $2 in the function is infact the first and second argument supplied after the function name when it's called for within the script, so you could say that a function is like a separate scrip in the main script. ------------------------------------------------------------------------------ Yet another example of a function is this: ------------------------------------------------------------------------------ #!/bin/bash myvar="$1" if [ "$myvar" = "" ]; then echo "Usage: $0 " exit 0 fi function calcfunc { expr 12 + $1 ; } myvar2=`calcfunc 5` echo "welcome" echo "Now we will calculate 12 + 5 * $myvar" echo "the answer is `expr $myvar2 '*' $myvar`" ------------------------------------------------------------------------------- Which executed looks like this: Without any arguments: alien:~$ ./funk.sh Usage: ./funk.sh alien:~$ And with arguments: alien:~$ ./funk.sh 23 welcome Now we will calculate 12 + 5 * 23 the answer is 391 alien:~$ ------------------------------------------------------------------------------ And for the sake of knowlidge it should also be said that a function can be declared in the following ways aswell: ------------------------------------------------------------------------------ #!/bin/bash function foo() { echo "hello world" } foo ------------------------------------------------------------------------------ #!/bin/bash foo () { echo "hello world" } foo ------------------------------------------------------------------------------ Note that the paranteses after the funtion name are the new thing here. It's used exactly the same way as without the paranteses, I just added that here so that you wont get confused if you see it made that way sometime. ------------------------------------------------------------------------------ So if you make a function, to call for it (to make use of it), just use the the functions name just as if it had been a command. If there is anything that's uncertain at this point, go back and read it again, until you understand it, or at least get the basic idea. =) =============================================================================== 3 - 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 world\n"); 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. =============================================================================== 5 - Practical Scripting Examples. =============================================================================== I'd first like to add a note which you already probably knows: ./ == look in current directory instead of the "PATH". To give that an example, say now that you have a script in your home directory called "ls" or "dir", how would you execute that without getting the contents of the directory ? well, that's why you use "./" before a name to execute it if it's in the current directory. ../ == previous directory (one directory further up towards "/" then you are currently in), this can be used as this, say that you have a script called "script" in "/home/user/" and you are standing in "/home/user/nice/" and you don't want to leave the directory but still want to execute the script. Then you do, "../script" and if you are in "/home/user/nice/blah/" you you would do, "../../script" ----- "../../" == 2 directory's back. Get the idea ? Anyway, now to the practical examples, which are working scripts for various purposes, to give an idea about what you can do with shell scripting. New things previously not explained will show up in this section, but I will explain them as we go along. Let's start with simple examples and move on to harder parts. As for first I'll stick to useless scripts =) just for illustration. Explanation on the scripts follow after them, as usual. So here we go on that. ------------------------------------------------------------------------------- #!/bin/bash one="$1" something="$2" if [ "$one" = "" ]; then echo "Usage: $0 [name] " exit 0 fi function first { clear echo "this is a test script !" echo echo "name followed on $0 was - $one - " echo echo "if you typed anything after the name it was: $something" echo } first exit 0 ------------------------------------------------------------------------------- Executed without any thing after the script name it looks like this: alien:~$ ./script Usage: ./script [name] alien:~$ Executed with a name it looks like this: alien:~$ ./script Jerry ---------------------------------------- this is a test script ! name followed on ./script was - Jerry - if you typed anything after the name it was: alien:~$ Executed with a name and something else it looks like this: alien:~$ ./script Jerry homer --------------------------------------- this is a test script ! name followed on ./script was - Jerry - if you typed anything after the name it was: homer alien:~$ ------------------------------------------------------------------------------- Notes: $0 == the script name's variable so you can do a "Usage: " whatever the script is named after you made it. $1 == the first thing that's typed after the script in the command line $2 == the second thing that's typed after the script in the command line $3, $4 and so on ..... one="$1" this puts the contents of "$1" into the variable $one which can be very useful to avoid errors. clear == clears the screen ------------------------------------------------------------------------------- This next example is a script which you really shouldn't use... It's here as an example for a working help script, but *could* cause harm if not used correctly. So if you loose anything because of using it, it's all on you. and don't say I didn't warn you. ------------------------------------------------------------------------------- #!/bin/bash if whoami | grep -v root >> /dev/null; then echo "you have to be root to use this" exit 1 else cat /etc/passwd | cut -f1 -d : | grep -v halt | grep -v operator | \ grep -v root | grep -v shutdown | grep -v sync | grep -v bin | \ grep -v ftp | grep -v daemon | grep -v adm | grep -v lp | \ grep -v mail | grep -v postmaster | grep -v news | grep -v uucp | \ grep -v man | grep -v games | grep -v guest | grep -v nobody > user.list fi for USER in `cat user.list`; do if cat /home/$USER/.bash_history | grep passwd >> /dev/null; then echo echo "user $USER have tried to access the passwd file" echo "do you want to remove $USER from your system [y/n] " read YN if [ "$YN" = "y" ]; then echo "user $USER is being deleted" echo "home dir of user $USER is however intact" echo remuser $USER else echo "user $USER is not deleted" echo fi else echo "$USER haven't tried to access the passwd file" fi done rm user.list echo echo "Script finished" echo exit 0 ------------------------------------------------------------------------------- I will just translate this script into real/clear English: if (check own user-name) is anything else but root >> send output to a black hole say, "you have to be root to use this" terminate program. in other cases (in this case that can only be if the user is root) list the contents of the file "/etc/passwd" combined with - cut out the user names (field 1 separated by ":") grep everything except lines containing the following words/names: halt operator root shutdown sync bin ftp daemon adm lp mail postmaster news uucp man games guest nobody and send it to the file "user.list" end "if" statement for each user in the "user.list" file do the following if the word "passwd" is present in "/home/$USER/.bash_history" >> output to the systems black hole say nothing say "user $USER haver tried to access the passwd file" say "do you want to remove $USER from your system [y/n]" read if the input from the keyboard is a "y" or "n" if the variable for the answer of the input is "y" then say "user $USER is being deleted" say "home dir of user $USER is however intact" say nothing removing the user from the system that tried to access the passwd file in other cases say "user $USER is not deleted" say nothing end "if" statement in other cases say $USER haven't tried to access the passwd file end "if" statement exit the for-loop remove the "user.list" file say nothing say "Script finished" say nothing exit and return to the shell command line. ------------------------------------------------------------------------------- Note: grep -v == show everything *except* what comes after the -v ------------------------------------------------------------------------------- Here is another way of doing the exact same script, just to illustrate that the same thing can be done in several different ways: ------------------------------------------------------------------------------- #!/bin/bash if [ "$UID" != "0" ]; then echo "you have to be root to use this" exit -1 fi for uids in `cat /etc/passwd`; do useruid=`echo "$uids" | awk -F':' '{print $(3)}'` test "$useruid" -ge "500" 2>/dev/null && echo "$uids" | awk -F':' '{print $(1)}' > user.list done for USER in `cat user.list`; do if (grep passwd /home/$USER/.bash_history >/dev/null); then echo echo "user $USER have tried to access the passwd file" echo "do you want to remove $USER from your system [y/n] " read YN case $YN in y* | Y*) echo "user $USER is being deleted" echo "home dir of user $USER is however intact" remuser $USER echo ;; n* | N*) echo "user $USER is not deleted" echo ;; esac else echo "$USER haven't tried to access the passwd file" rm -f user.list echo echo "Script finished" echo fi done exit 0 ------------------------------------------------------------------------------- Since this script does the exact same thing, but in another way, I'll leav you with the experiance of trying to figure out the differences and how it works with the help of this tutorial, you might not get this right until you've read this tutorial twice. A tip is: try to make a comment to each line with what it does and why. ------------------------------------------------------------------------------- This below script is a "Wingate" scanner, to scan for wingates that can be used to bounce off and such things, don't know if that's really legal so take that on your own risk. Anyway here comes the script: ------------------------------------------------------------------------------- #!/bin/bash echo > .log.tmp echo > .log2.tmp echo "sleep 7" > wg.config echo "killall -2 telnet" >> wg.config scan="$1" count="0" max="255" clear if whoami | grep root >> /dev/null ; then echo "please use this as user and not root, since it would kill all users" echo "telnet sessions" else clear fi if [ "$1" = "" ]; then echo " usage is: $0 " echo " examples:" echo " $0 127.0.0" echo " That will scan from 127.0.0.0 to 127.0.0.255" echo echo "be aware though, while it scans it also kills any other telnet" echo "sessions you might have ...." exit 0 fi while [ "$count" != "$max" ]; do count=`expr $count + 1` echo "Attempting connection to $1.$count " echo > .log2.tmp ./wg.config & telnet $scan.$count >> .log.tmp cat .log.tmp | grep -v refused | grep -v closed | grep -v Connected | grep -v Escape | grep -v login >> .log2.tmp echo >> .log.tmp done echo "Script Finished, results stored in .log.tmp and .log2.tmp" exit 0 ------------------------------------------------------------------------------- This time I will not translate the script into clear English and I will not show how it looks executed, I leave that for you to do =) ------------------------------------------------------------------------------ Now a final practical example of a script, this is a small graphical front end to the console mp3 player `mpg123` so you got to have that to work and you need to execute this script in a directly where you have mp3's .... Also if you want the X-windows part of it to work you need to get and install Xdialog, you can get that from www.freshmeat.net ... However if you have Linux Mandrake you should be good anyway, Xdialog comes as default in Mandrake. This script should be named `xmpg123`. So here we go: ------------------------------------------------------------------------------- #!/bin/bash dialog --backtitle "xmpg123" \ --title "Main menu" \ --menu "Make your choice" 13 60 6 \ 1 "X-windows" \ 2 "Ncurses" \ 3 "Exit" 2> .tempfile output=`cat .tempfile` echo $output rm -f .tempfile if [ "$output" = "1" ]; then for songs in `ls -1 *.mp3`; do echo "$songs mp3-file" >> .tempfile done output=`cat .tempfile` Xdialog --menu 2 2 2 1 $output \ 2> .tempfile output=`cat .tempfile` mpg123 $output rm -f .tempfile fi if [ "$output" = "2" ]; then for songs in `ls -1 *.mp3`; do echo "$songs mp3-file" >> .tempfile done menu=`cat .tempfile` dialog --menu "Make your choice" 13 70 6 $menu 2> .tempfile output=`cat .tempfile` mpg123 $output rm -f .tempfile fi exit 0 ------------------------------------------------------------------------------- A note being that dialog and Xdialog seems to be in early stages, so this may look sort of buggy if you don't have the great dialog god at your side... ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- And don't forget to "chmod u+x