LDP/LDP/howto/linuxdoc/Bash-Prog-Intro-HOWTO.sgml

1177 lines
33 KiB
Plaintext
Raw Normal View History

2000-06-12 18:09:51 +00:00
<!doctype linuxdoc system>
<!--
2000-06-12 18:09:51 +00:00
An introduction to bash shell programming
-->
<!-- Title information -->
<article>
<title>BASH Programming - Introduction HOW-TO</title>
2000-07-27 13:51:54 +00:00
<author>by Mike G <tt/mikkey at dynamo.com.ar/</author>
<date>
2000-07-27 13:51:54 +00:00
Thu Jul 27 09:36:18 ART 2000
</date>
<abstract>
This article is intended to help you to start programming
basic-intermediate shell scripts. It does not intend to be an
advanced document (see the title). I am NOT an expert nor guru
shell programmer. I decided to write this because I'll learn a
lot and it might be useful to other people. Any feedback is
appreciated, especially in the patch or pull request form. :)
2000-06-12 18:09:51 +00:00
</abstract>
<!-- Table of contents -->
<toc>
<!-- Requisites -->
<sect>
Introduction
2000-06-20 14:41:52 +00:00
2000-06-22 18:54:39 +00:00
2000-07-05 17:23:44 +00:00
<sect1>
Getting the latest version
2000-06-12 18:09:51 +00:00
2000-07-05 17:23:44 +00:00
<p>
2019-04-01 16:22:16 +00:00
<htmlurl url="https://www.tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html"
name="https://www.tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html">
2000-06-12 18:09:51 +00:00
</sect1>
2000-06-22 18:54:39 +00:00
2000-06-12 18:09:51 +00:00
<sect1>
Requisites
<P> Familiarity with GNU/Linux command lines, and familiarity
2000-06-12 18:09:51 +00:00
with basic programming concepts is helpful. While this is not
a programming introduction, it explains (or at least tries) many
basic concepts.
<P>
2000-06-12 18:09:51 +00:00
</sect1>
<sect1>
Uses of this document
<P> This document tries to be useful in the following situations
<itemize>
<item> You have an idea about programming and you want to start
2000-06-12 18:09:51 +00:00
coding some shell scripts.
<item> You have a vague idea about shell programming and want
2000-06-12 18:09:51 +00:00
some sort of reference.
<item> You want to see some shell scripts and some comments to
start writing your own.
<item> You are migrating from DOS/Windows (or already did)
2000-06-12 18:09:51 +00:00
and want to make "batch" processes.
<item> You are a complete nerd and read every how-to available.
2000-06-12 18:09:51 +00:00
</itemize>
</sect1>
</sect>
<!-- Very simple Scripts -->
2000-06-12 18:09:51 +00:00
<sect>
Very simple Scripts
<P> This HOW-TO will try to give you some hints about shell script
programming strongly based on examples.
<P> In this section you'll find some little scripts which
2000-06-12 18:09:51 +00:00
will hopefully help you to understand some techniques.
<sect1>
Traditional hello world script
<!-- __TO-DO__
someone suggested using this instead of the line numbering
2000-06-12 18:09:51 +00:00
but it doesn't work on verion 1.0.9
<pre></pre>
2000-06-12 18:09:51 +00:00
-->
<P>
<tscreen><verb>
#!/bin/bash
echo Hello World
2000-06-12 18:09:51 +00:00
</verb></tscreen>
<P>
<P> This script has only two lines.
The first indicates the system which program
to use to run the file.
<P> The second line is the only action performed by this script,
2000-06-22 18:54:39 +00:00
which prints 'Hello World' on the terminal.
2000-07-05 17:23:44 +00:00
<P> If you get something like <it>./hello.sh: Command not found.</it>
Probably the first line '#!/bin/bash' is wrong, issue whereis bash or see
'finding bash' to see how should you write this line.
2000-06-12 18:09:51 +00:00
</sect1>
<sect1>
A very simple backup script
<P>
<tscreen><verb>
#!/bin/bash
tar -czf /var/my-backup.tgz /home/me/
2000-06-12 18:09:51 +00:00
</verb></tscreen>
<P> In this script, instead of printing a message on the terminal,
2000-06-12 18:09:51 +00:00
we create a tar-ball of a user's home directory. This is NOT intended
to be used, a more useful backup script is presented later in this
2000-06-12 18:09:51 +00:00
document.
</sect1>
2000-06-12 18:09:51 +00:00
</sect>
2000-06-20 14:41:52 +00:00
<!-- Redirection -->
<sect>
All about redirection
<sect1>
Theory and quick reference
2000-06-22 18:54:39 +00:00
<p> There are 3 file descriptors, stdin, stdout and stderr (std=standard).
2000-06-20 14:41:52 +00:00
<p>Basically you can:
<enum>
<item> redirect stdout to a file
<item> redirect stderr to a file
<item> redirect stdout to a stderr
<item> redirect stderr to a stdout
<item> redirect stderr and stdout to a file
<item> redirect stderr and stdout to stdout
2000-06-20 14:41:52 +00:00
<item> redirect stderr and stdout to stderr
</enum>
2000-06-22 18:54:39 +00:00
1 'represents' stdout and 2 stderr.
<p> A little note for seeing the output of these commands: with the less
command you can view both stdout (which will remain on the buffer) and the
stderr that will be printed on the screen, but erased as you try to 'browse'
the buffer.
2000-06-20 14:41:52 +00:00
</sect1>
2000-06-20 14:41:52 +00:00
<sect1>
Sample: stdout 2 file
<p> This will cause the output of a program to be written to a file.
2000-06-20 14:41:52 +00:00
<tscreen><verb>
ls -l > ls-l.txt
</verb></tscreen>
Here, a file called 'ls-l.txt' will be created and it will contain what you would see on the
2000-06-20 14:41:52 +00:00
screen if you type the command 'ls -l' and execute it.
</sect1>
<sect1>
Sample: stderr 2 file
<p> This will cause the stderr output of a program to be written to a file.
2000-06-20 14:41:52 +00:00
<tscreen><verb>
grep da * 2> grep-errors.txt
</verb></tscreen>
Here, a file called 'grep-errors.txt' will be created and it will contain what you would see
the stderr portion of the output of the 'grep da *' command.
</sect1>
<sect1>
Sample: stdout 2 stderr
<p> This will cause the stdout output of a program to be written to the same filedescriptor
as stderr.
2000-06-20 14:41:52 +00:00
<tscreen><verb>
grep da * 1>&2
2000-06-20 14:41:52 +00:00
</verb></tscreen>
Here, the stdout portion of the command is sent to stderr, you may notice that in different ways.
2000-06-20 14:41:52 +00:00
</sect1>
<sect1>
Sample: stderr 2 stdout
<p> This will cause the stderr output of a program to be written to the same filedescriptor
as stdout.
2000-06-20 14:41:52 +00:00
<tscreen><verb>
grep * 2>&1
</verb></tscreen>
Here, the stderr portion of the command is sent to stdout, if you pipe to less, you'll see that
lines that normally 'disappear' (as they are written to stderr) are being kept now (because
they're on stdout).
2000-06-20 14:41:52 +00:00
</sect1>
<sect1>
Sample: stderr and stdout 2 file
<p> This will place every output of a program to a file. This is suitable sometimes
for cron entries, if you want a command to pass in absolute silence.
2000-06-20 14:41:52 +00:00
<tscreen><verb>
rm -f $(find / -name core) &> /dev/null
2000-06-20 14:41:52 +00:00
</verb></tscreen>
This (thinking on the cron entry) will delete every file called 'core' in any directory. Notice
2019-04-01 16:21:10 +00:00
that you should be pretty sure of what a command is doing if you are going to wipe its output.
2000-06-20 14:41:52 +00:00
</sect1>
<!--
redirect stderr and stdout to stdout
2000-06-20 14:41:52 +00:00
redirect stderr and stdout to stderr
-->
<!--
<sect1>
Sample:
<p> This will
2000-06-20 14:41:52 +00:00
<tscreen><verb>
command
</verb></tscreen>
Explanation
</sect1>
-->
</sect>
<!-- pipes -->
<sect>
Pipes
<p> This section explains in a very simple and practical way how to use pipes,
and why you may want to use them.
2000-06-20 14:41:52 +00:00
<sect1>
What they are and why you'll want to use them
<p> Pipes let you use (very simple, I insist) the output of a program as the
input of another one
2000-06-20 14:41:52 +00:00
</sect1>
<sect1>
Sample: simple pipe with sed
2000-06-20 14:41:52 +00:00
<p> This is very simple way to use pipes.
<tscreen><verb>
ls -l | sed -e "s/[aeio]/u/g"
2000-06-20 14:41:52 +00:00
</verb></tscreen>
Here, the following happens: first the command ls -l is executed, and its output,
2000-06-20 14:41:52 +00:00
instead of being printed, is sent (piped) to the sed program, which in turn, prints
what it has to.
2000-06-20 14:41:52 +00:00
</sect1>
<sect1>
Sample: an alternative to ls -l *.txt
2000-06-20 14:41:52 +00:00
<p> Probably, this is a more difficult way to do ls -l *.txt, but it is here for illustrating pipes,
not for solving such listing dilemma.
2000-06-20 14:41:52 +00:00
<tscreen><verb>
ls -l | grep "\.txt$"
</verb></tscreen>
Here, the output of the program ls -l is sent to the grep program, which, in turn, will print
2000-06-20 14:41:52 +00:00
lines which match the regex "\.txt$".
</sect1>
<!--
<sect1>
Sample:
<p> This will
2000-06-20 14:41:52 +00:00
<tscreen><verb>
command
</verb></tscreen>
Explanation
</sect1>
-->
</sect>
2000-06-12 18:09:51 +00:00
<!-- Variables -->
<sect>
Variables
<!-- Dry Theory -->
<P> You can use variables as in any programming languages.
2000-06-12 18:09:51 +00:00
There are no data types. A variable in bash can contain a number, a
character, a string of characters.
<P> You have no need to declare a variable, just
2000-06-12 18:09:51 +00:00
assigning a value to its reference will create it.
2000-06-12 18:09:51 +00:00
<!-- Samples -->
<sect1>
Sample: Hello World! using variables
<P>
2000-06-12 18:09:51 +00:00
<tscreen><verb>
#!/bin/bash
2000-06-12 18:09:51 +00:00
STR="Hello World!"
echo $STR
2000-06-12 18:09:51 +00:00
</verb></tscreen>
<P> Line 2 creates
2000-06-12 18:09:51 +00:00
a variable called STR and assigns the string "Hello World!" to
it. Then the VALUE of this variable is retrieved by putting
the '$' in at the beginning. Please notice (try it!)
2000-06-12 18:09:51 +00:00
that if you don't use the '$' sign, the output of the program will
be different, and probably not what you want it to be.
</sect1>
2000-06-22 18:54:39 +00:00
2000-06-12 18:09:51 +00:00
<sect1>
Sample: A very simple backup script (little bit better)
<P>
2000-06-12 18:09:51 +00:00
<tscreen><verb>
#!/bin/bash
2000-06-12 18:09:51 +00:00
OF=/var/my-backup-$(date +%Y%m%d).tgz
tar -czf $OF /home/me/
2000-06-12 18:09:51 +00:00
</verb></tscreen>
<P> This script introduces another thing. First
of all, you should be familiarized with the variable
2000-06-12 18:09:51 +00:00
creation and assignation on line 2. Notice the expression
'$(date +%Y%m%d)'.
If you run the script you'll notice that
2000-06-12 18:09:51 +00:00
it runs the
command inside the parenthesis, capturing its output.
2000-06-12 18:09:51 +00:00
<P> Notice that in this script, the output filename will
2000-06-22 18:54:39 +00:00
be different every day, due to the format switch to the date command(+%Y%m%d).
2000-06-12 18:09:51 +00:00
You can change this by specifying a different format.
<P> Some more examples:
<P> echo ls
<P> echo $(ls)
</sect1>
2000-06-22 18:54:39 +00:00
<sect1>
Local variables
<p> Local variables can be created by using the keyword <it/local/.
<tscreen><verb>
#!/bin/bash
HELLO=Hello
2000-06-22 18:54:39 +00:00
function hello {
local HELLO=World
echo $HELLO
}
echo $HELLO
hello
echo $HELLO
</verb></tscreen>
<p> This example should be enought to show how to use a local variable.
2000-06-22 18:54:39 +00:00
</sect1>
2000-06-12 18:09:51 +00:00
</sect>
<!-- Conditionals -->
<sect>
Conditionals
<P> Conditionals let you decide whether to perform an action
2000-06-12 18:09:51 +00:00
or not, this decision is taken by evaluating an expression.
<!-- Dry Theory -->
<sect1>
Dry Theory
<P> Conditionals have many forms. The most basic form is:
2000-07-18 14:15:50 +00:00
<bf>if</bf> <it/expression/ <bf>then</bf> <it/statement/
where 'statement' is only executed if 'expression'
2000-06-12 18:09:51 +00:00
evaluates to true.
'2<1' is an expresion that evaluates to false, while '2>1'
2000-06-12 18:09:51 +00:00
evaluates to true.xs
2000-07-18 14:15:50 +00:00
<p> Conditionals have other forms such as:
<bf/if/ <it/expression/
2000-06-12 18:09:51 +00:00
<bf/then/ <it/statement1/ <bf/else/ <it/statement2/.
Here 'statement1' is executed if 'expression' is true,otherwise
'statement2' is executed.
<P> Yet another form of conditionals is:
<bf/if/ <it/expression1/
<bf/then/ <it/statement1/
<bf/else if/ <it/expression2/ <bf/then/ <it/statement2/
2000-06-12 18:09:51 +00:00
<bf/else/ <it/statement3/.
In this form there's added only the
"ELSE IF 'expression2' THEN 'statement2'" which makes statement2 being
executed if expression2 evaluates to true. The rest is as you may
2000-06-12 18:09:51 +00:00
imagine (see previous forms).
<P> A word about syntax:
<P> The base for the 'if' constructions in bash is this:
<P> if [expression];
<P> then
<P> code if 'expression' is true.
<P> fi
</sect1>
<!-- Samples -->
<sect1>
Sample: Basic conditional example if .. then
<P>
2000-06-12 18:09:51 +00:00
<tscreen><verb>
#!/bin/bash
if [ "foo" = "foo" ]; then
echo expression evaluated as true
fi
</verb></tscreen>
<P> The code to be executed if the expression within braces
is true can
be found after the 'then' word and before 'fi' which indicates the end
2000-06-12 18:09:51 +00:00
of the conditionally executed code.
</sect1>
<sect1>
Sample: Basic conditional example if .. then ... else
<P>
<tscreen><verb>
#!/bin/bash
if [ "foo" = "foo" ]; then
echo expression evaluated as true
else
echo expression evaluated as false
fi
</verb></tscreen>
</sect1>
<sect1>
Sample: Conditionals with variables
<P>
2000-06-12 18:09:51 +00:00
<tscreen><verb>
#!/bin/bash
T1="foo"
T2="bar"
2000-07-05 17:23:44 +00:00
if [ "$T1" = "$T2" ]; then
2000-06-12 18:09:51 +00:00
echo expression evaluated as true
else
echo expression evaluated as false
fi
</verb></tscreen>
</sect1>
2000-06-21 17:28:14 +00:00
<!--
<sect1>
Sample: testing if a file exists
<P> one more thank to mike
2000-06-21 17:28:14 +00:00
<tscreen><verb>
#!/bin/bash
FILE=~/.basrc
if [ -f $FILE ]; then
echo file $FILE exits
else
echo file not found
fi
if [ 'test -f $FILE']
</verb></tscreen>
</sect1>
-->
2000-06-12 18:09:51 +00:00
</sect>
<!-- Loops : for, while and until -->
<sect>
Loops for, while and until
<!-- Dry Theory -->
<P> In this section you'll find for, while and until loops.
<P> The <bf/for/ loop is a little bit different from other programming
languages. Basically, it let's you iterate over a series of
2000-06-12 18:09:51 +00:00
'words' within a string.
<P> The <bf/while/ executes a piece of code if the control expression
is true, and only stops when it is false (or a explicit break is found
within the executed code.
<P> The <bf/until/ loop is almost equal to the while loop, except that
the code is executed while the control expression evaluates to false.
<P> If you suspect that while and until are very similar you are right.
<!-- Samples -->
2000-06-12 18:09:51 +00:00
<sect1>
For sample
<P>
2000-06-12 18:09:51 +00:00
<tscreen><verb>
#!/bin/bash
for i in $( ls ); do
echo item: $i
done
</verb></tscreen>
<p>
2000-06-12 18:09:51 +00:00
<p> On the second line, we declare i to be the variable that
will take the
2000-06-12 18:09:51 +00:00
different values contained in $( ls ).
<p> The third line could be longer if needed, or there could
be more lines
2000-06-12 18:09:51 +00:00
before the done (4).
<p> 'done' (4) indicates that the code that used the value of $i has
finished and $i can take a new value.
<p> This script has very little sense, but a more useful way to use the
for loop would be to use it to match only certain files on the previous
example
<p>
</sect1>
2000-06-23 20:17:41 +00:00
<sect1>
C-like for
<P> fiesh suggested adding this form of looping. It's a for loop
more similar to C/perl... for.
<tscreen><verb>
#!/bin/bash
for i in `seq 1 10`;
do
echo $i
done
2000-06-23 20:17:41 +00:00
</verb></tscreen>
</sect1>
2000-06-12 18:09:51 +00:00
<!-- while -->
<sect1>
While sample
<P> <tscreen><verb>
#!/bin/bash
2000-06-12 18:09:51 +00:00
COUNTER=0
while [ $COUNTER -lt 10 ]; do
echo The counter is $COUNTER
let COUNTER=COUNTER+1
2000-06-12 18:09:51 +00:00
done
</verb></tscreen>
<P>
<P> This script 'emulates' the well known
2000-07-05 17:23:44 +00:00
(C, Pascal, perl, etc) 'for' structure
2000-06-12 18:09:51 +00:00
</sect1>
<!-- until -->
<sect1>
Until sample
<P> <tscreen><verb>
#!/bin/bash
2000-06-12 18:09:51 +00:00
COUNTER=20
until [ $COUNTER -lt 10 ]; do
echo COUNTER $COUNTER
let COUNTER-=1
done
</verb></tscreen>
</sect1>
</sect>
<!-- Functions -->
<sect>
Functions
<P> As in almost any programming language, you can use functions to group
2000-06-12 18:09:51 +00:00
pieces of code in a more logical way or practice the divine art of recursion.
<P> Declaring a function is just a matter of writing function my_func { my_code }.
<P> Calling a function is just like calling another program, you just write its name.
<P>
<sect1>
Functions sample
<P> <tscreen><verb>
#!/bin/bash
2000-06-22 18:54:39 +00:00
function quit {
exit
}
function hello {
echo Hello!
}
hello
quit
echo foo
2000-06-12 18:09:51 +00:00
</verb></tscreen>
<P> Lines 2-4 contain the 'quit' function. Lines 5-7 contain the 'hello' function
If you are not absolutely sure about what this script does, please try it!
<P> Notice that functions don't need to be declared in any specific order.
<P> When running the script you'll notice that first: the function 'hello' is
2000-06-12 18:09:51 +00:00
called, second the 'quit' function, and the program never reaches line 10.
</sect1>
2000-06-22 18:54:39 +00:00
<sect1>
Functions with parameters sample
<P> <tscreen><verb>
#!/bin/bash
2000-06-22 18:54:39 +00:00
function quit {
exit
}
2000-06-22 18:54:39 +00:00
function e {
echo $1
}
2000-06-22 18:54:39 +00:00
e Hello
e World
quit
echo foo
2000-06-22 18:54:39 +00:00
</verb></tscreen>
2019-10-31 02:22:40 +00:00
<P> This script is almost identical to the previous one. The main difference is the
function 'e'. This function, prints the first argument it receives.
Arguments, within functions, are treated in the same manner as arguments given to the script.
2000-06-22 18:54:39 +00:00
</sect1>
2000-06-12 18:09:51 +00:00
</sect>
<!-- User interfaces -->
<sect>
User interfaces
<!-- Simple menus with select -->
<sect1>
Using select to make simple menus
<P>
<tscreen><verb>
#!/bin/bash
OPTIONS="Hello Quit"
select opt in $OPTIONS; do
2000-07-05 17:23:44 +00:00
if [ "$opt" = "Quit" ]; then
2000-06-12 18:09:51 +00:00
echo done
exit
2000-07-05 17:23:44 +00:00
elif [ "$opt" = "Hello" ]; then
2000-06-12 18:09:51 +00:00
echo Hello World
else
clear
echo bad option
fi
done
</verb></tscreen>
<P> If you run this script you'll see that it is a
programmer's dream for text based menus. You'll probably notice
that it's very similar to the 'for' construction, only rather
2000-06-12 18:09:51 +00:00
than looping for each 'word' in $OPTIONS, it prompts the user.
<P>
</sect1>
<!-- Using the command line -->
<sect1>
Using the command line
2000-06-12 18:09:51 +00:00
<P>
<tscreen><verb>
#!/bin/bash
if [ -z "$1" ]; then
2000-06-12 18:09:51 +00:00
echo usage: $0 directory
exit
fi
SRCD=$1
TGTD="/var/backups/"
OF=home-$(date +%Y%m%d).tgz
tar -czf $TGTD$OF $SRCD
2000-06-12 18:09:51 +00:00
</verb></tscreen>
<P>
<P> What this script does should be clear to you. The expression
2000-06-12 18:09:51 +00:00
in the first conditional tests if the program has received an argument
($1) and quits if it didn't, showing the user a little usage message.
The rest of the script should be clear at this point.
</sect1>
</sect>
<!-- Misc -->
<sect>
Misc
<!-- Reading user input -->
2000-06-12 18:09:51 +00:00
<sect1>
2000-06-22 18:54:39 +00:00
Reading user input with read
<P> In many occasions you may want to prompt the user for some input, and
there are several ways
2000-06-22 18:54:39 +00:00
to achive this. This is one of those ways:
<tscreen><verb>
#!/bin/bash
echo Please, enter your name
read NAME
echo "Hi $NAME!"
</verb></tscreen>
<P> As a variant, you can get multiple values with read, this example may
2000-07-05 17:23:44 +00:00
clarify this.
2000-06-22 18:54:39 +00:00
<tscreen><verb>
#!/bin/bash
echo Please, enter your firstname and lastname
read FN LN
2000-06-22 18:54:39 +00:00
echo "Hi! $LN, $FN !"
</verb></tscreen>
2000-06-12 18:09:51 +00:00
</sect1>
<!-- Arithmetic evaluation -->
2000-06-12 18:09:51 +00:00
<sect1>
Arithmetic evaluation
<P> On the command line (or a shell) try this:
<P> echo 1 + 1
<P> If you expected to see '2' you'll be disappointed. What if
2000-06-12 18:09:51 +00:00
you want BASH to evaluate some numbers you have? The solution
is this:
<P> echo $((1+1))
<P> This will produce a more 'logical' output. This is to evaluate an
arithmetic expression. You can achieve this also like this:
<P> echo $[1+1]
<P>
<P> If you need to use fractions, or more math or you just want it, you
2000-06-12 18:09:51 +00:00
can use bc to evaluate arithmetic expressions.
<P> If I ran "echo $[3/4]" at the command prompt, it would return 0
2000-06-12 18:09:51 +00:00
because bash only uses integers when answering. If you ran
"echo 3/4|bc -l", it would properly return 0.75.
</sect1>
2000-06-21 17:28:14 +00:00
<sect1>
Finding bash
<P> From a message from mike (see Thanks to)
2000-06-23 20:17:41 +00:00
<P> you always use #!/bin/bash .. you might was to give an example of
<P> how to find where bash is located.
<P> 'locate bash' is preferred, but not all machines have locate.
<P> 'find ./ -name bash' from the root dir will work, usually.
<P> Suggested locations to check:
<P> ls -l /bin/bash
<P> ls -l /sbin/bash
<P> ls -l /usr/local/bin/bash
<P> ls -l /usr/bin/bash
<P> ls -l /usr/sbin/bash
<P> ls -l /usr/local/sbin/bash
<P> (can't think of any other dirs offhand... i've found it in
<P> most of these places before on different system).
2000-07-18 14:15:50 +00:00
<P> You may try also 'which bash'.
2000-06-21 17:28:14 +00:00
</sect1>
<!-- Capturing a commands output -->
2000-06-12 18:09:51 +00:00
<sect1>
2000-06-21 17:28:14 +00:00
Getting the return value of a program
<P> In bash, the return value of a program is stored in a special variable called $?.
<P> This illustrates how to capture the return value of a program, I assume that the directory
2000-06-22 18:54:39 +00:00
<it/dada/ does not exist. (This was also suggested by mike)
2000-06-21 17:28:14 +00:00
<tscreen><verb>
#!/bin/bash
cd /dada &> /dev/null
echo rv: $?
cd $(pwd) &> /dev/null
echo rv: $?
</verb> </tscreen>
</sect1>
<!-- Capturing a commands output -->
2000-06-21 17:28:14 +00:00
<sect1>
Capturing a commands output
<P> This little script shows all tables from all databases (assuming you got MySQL installed).
2000-06-21 17:28:14 +00:00
Also, consider changing the 'mysql' command to use a valid username and password.
<tscreen><verb>
#!/bin/bash
DBS=`mysql -uroot -e"show databases"`
for b in $DBS ;
do
mysql -uroot -e"show tables from $b"
done
</verb></tscreen>
2000-06-12 18:09:51 +00:00
</sect1>
2000-06-12 18:09:51 +00:00
<!-- Multiple source files -->
<sect1>
Multiple source files
<P> You can use multiple files with the command source.
2000-06-21 17:28:14 +00:00
<P> __TO-DO__
2000-06-12 18:09:51 +00:00
</sect1>
</sect>
<!-- Tables -->
<sect>
Tables
<!-- String operators -->
<sect1>
String comparison operators
2000-06-12 18:09:51 +00:00
<P> (1) s1 = s2
<P> (2) s1 != s2
<P> (3) s1 < s2
<P> (4) s1 > s2
<P> (5) -n s1
<P> (6) -z s1
<P>
2000-06-12 18:09:51 +00:00
<P> (1) s1 matches s2
<P> (2) s1 does not match s2
<P> (3) __TO-DO__
<P> (4) __TO-DO__
<P> (5) s1 is not null (contains one or more characters)
<P> (6) s1 is null
2000-06-12 18:09:51 +00:00
</sect1>
2000-06-22 18:54:39 +00:00
<sect1>
String comparison examples
<p> Comparing two strings.
2000-06-22 18:54:39 +00:00
<tscreen><verb>
#!/bin/bash
S1='string'
S2='String'
if [ $S1=$S2 ];
then
echo "S1('$S1') is not equal to S2('$S2')"
fi
if [ $S1=$S1 ];
then
echo "S1('$S1') is equal to S1('$S1')"
fi
</verb></tscreen>
2000-07-05 17:23:44 +00:00
<p> I quote here a note from a mail, sent buy Andreas Beck, refering to use
<it/if [ $1 = $2 ]/.
<p> This is not quite a good idea, as if either $S1 or $S2 is empty, you will
get a parse error. x$1=x$2 or "$1"="$2" is better.
2000-06-22 18:54:39 +00:00
</sect1>
<!-- Arithmetic operators -->
2000-06-12 18:09:51 +00:00
<sect1>
Arithmetic operators
<P> +
<P> -
<P> *
<P> /
2000-07-05 17:23:44 +00:00
<P> % (remainder)
2000-06-12 18:09:51 +00:00
</sect1>
<!-- Arithmetic relational operators -->
2000-06-12 18:09:51 +00:00
<sect1>
Arithmetic relational operators
<P> -lt (<)
2000-06-12 18:09:51 +00:00
<P> -gt (>)
<P> -le (<=)
<P> -ge (>=)
<P> -eq (==)
<P> -ne (!=)
<P> C programmer's should simple map the operator to its corresponding
2000-06-12 18:09:51 +00:00
parenthesis.
</sect1>
<!-- Useful commands -->
2000-06-12 18:09:51 +00:00
<sect1>
Useful commands
<P> This section was re-written by Kees (see thank to...)
<P> Some of these commands almost contain complete programming languages.
From those commands only the basics will be explained. For a more detailed
description, have a closer look at the main pages of each command.
2000-07-05 17:23:44 +00:00
<bf/sed/ (stream editor)
<P> Sed is a non-interactive editor. Instead of altering a file by moving the
cursor on the screen, you use a script of editing instructions to sed, plus the
name of the file to edit. You can also describe sed as a filter. Let's have
2000-07-05 17:23:44 +00:00
a look at some examples:
<tscreen><verb>
$sed 's/to_be_replaced/replaced/g' /tmp/dummy
</verb></tscreen>
<P> Sed replaces the string 'to_be_replaced' with the string 'replaced' and
reads from the /tmp/dummy file. The result will be sent to stdout (normally
the console) but you can also add '> capture' to the end of the line above so
2000-07-05 17:23:44 +00:00
that sed sends the output to the file 'capture'.
<tscreen><verb>
$sed 12, 18d /tmp/dummy
</verb></tscreen>
2000-07-05 17:23:44 +00:00
<P> Sed shows all lines except lines 12 to 18. The original file is not altered by this command.
<bf/awk/ (manipulation of datafiles, text retrieval and processing)
<P> Many implementations of the AWK programming language exist (most known interpreters are GNU's
gawk and 'new awk' mawk.) The principle is simple: AWK scans for a pattern, and for every
matching pattern an action will be performed.
2000-07-05 17:23:44 +00:00
<P> Again, I've created a dummy file containing the following lines:
<P> <it/"test123/
<P> <it/test/
<P> <it/tteesstt"/
<tscreen><verb>
$awk '/test/ {print}' /tmp/dummy
</verb></tscreen>
<P> test123
<P> test
<P> The pattern AWK looks for is 'test' and the action it performs when it found a line in the file
/tmp/dummy with the string 'test' is 'print'.
<tscreen><verb>
$awk '/test/ {i=i+1} END {print i}' /tmp/dummy
</verb></tscreen>
<P> 3
<P> When you're searching for many patterns, you should replace the text between the quotes with '-f
file.awk' so you can put all patterns and actions in 'file.awk'.
2000-06-12 18:09:51 +00:00
2000-07-05 17:23:44 +00:00
<bf/grep/ (print lines matching a search pattern)
2000-06-12 18:09:51 +00:00
2000-07-05 17:23:44 +00:00
<P> We've already seen quite a few grep commands in the previous chapters, that display the lines
matching a pattern. But grep can do more.
<tscreen><verb>
$grep "look for this" /var/log/messages -c
</verb></tscreen>
<P> 12
<P> The string "look for this" has been found 12 times in the file /var/log/messages.
<P> [ok, this example was a fake, the /var/log/messages was tweaked :-)]
<bf/wc/ (counts lines, words and bytes)
<P> In the following example, we see that the output is not what we expected. The dummy file, as used
in this example, contains the following text:
<it/"bash introduction/
<it/ howto test file"/
<tscreen><verb>
$wc --words --lines --bytes /tmp/dummy
</verb></tscreen>
<P> 2 5 34 /tmp/dummy
<P> Wc doesn't care about the parameter order. Wc always prints them in a standard order, which is,
as you can see: &lt;lines&gt;&lt;words&gt;&lt;bytes&gt;&lt;filename&gt;.
2000-07-05 17:23:44 +00:00
<bf/sort/ (sort lines of text files)
<P> This time the dummy file contains the following text:
<P> <it/"b/
<P> <it/c/
<P> <it/a"/
<tscreen><verb>
$sort /tmp/dummy
</verb></tscreen>
2000-06-12 18:09:51 +00:00
2000-07-05 17:23:44 +00:00
<P> This is what the output looks like:
<P> <it/a/
<P> <it/b/
<P> <it/c/
<P> Commands shouldn't be that easy :-)
<bf/bc/ (a calculator programming language)
<P> Bc is accepting calculations from command line (input from file. not from redirector or pipe),
but also from a user interface. The following demonstration shows some of the commands. Note that
<P> I start bc using the -q parameter to avoid a welcome message.
<tscreen><verb>
$bc -q
</verb></tscreen>
<P> <it/1 == 5/
<P> <it/0/
<P> <it/0.05 == 0.05/
<P> <it/1/
<P> <it/5 != 5/
<P> <it/0/
<P> <it/2 ^ 8/
<P> <it/256/
<P> <it/sqrt(9)/
<P> <it/3/
<P> <it/while (i != 9) {/
<P> <it/i = i + 1;/
<P> <it/print i/
<P> <it/}/
2000-07-05 17:23:44 +00:00
<P> <it/123456789/
<P> <it/quit/
<bf/tput/ (initialize a terminal or query terminfo database)
<P> A little demonstration of tput's capabilities:
<tscreen><verb>
$tput cup 10 4
</verb></tscreen>
<P> The prompt appears at (y10,x4).
<tscreen><verb>
$tput reset
</verb></tscreen>
<P> Clears screen and prompt appears at (y1,x1). Note that (y0,x0) is the upper left corner.
<tscreen><verb>
$tput cols
</verb></tscreen>
<it/80/
<P> Shows the number of characters possible in x direction.
<P> It is higly recommended to be familiarized with these programs (at least). There are tons of
2000-07-05 17:23:44 +00:00
little programs that will let you do real magic on the command line.
<P> [some samples are taken from man pages or FAQs]
</sect1>
2000-06-12 18:09:51 +00:00
</sect>
<!-- More Scripts -->
<sect>
More Scripts
<sect1>
Applying a command to all files in a directory.
<P>
2000-06-12 18:09:51 +00:00
</sect1>
<sect1>
Sample: A very simple backup script (little bit better)
<P>
<tscreen><verb>
#!/bin/bash
2000-06-12 18:09:51 +00:00
SRCD="/home/"
TGTD="/var/backups/"
OF=home-$(date +%Y%m%d).tgz
tar -czf $TGTD$OF $SRCD
</verb></tscreen>
2000-06-12 18:09:51 +00:00
<P>
</sect1>
<sect1>
File re-namer
<P>
<tscreen><verb>
2000-06-12 18:09:51 +00:00
#!/bin/sh
# renna: rename multiple files according to several rules
# written by felix hudson Jan - 2000
2000-06-12 18:09:51 +00:00
#first check for the various 'modes' that this program has
#if the first ($1) condition matches then we execute that portion of the
#program and then exit
2000-06-12 18:09:51 +00:00
# check for the prefix condition
if [ $1 = p ]; then
2000-06-12 18:09:51 +00:00
#we now get rid of the mode ($1) variable and prefix ($2)
prefix=$2 ; shift ; shift
2000-06-12 18:09:51 +00:00
# a quick check to see if any files were given
# if none then its better not to do anything than rename some non-existent
# files!!
2000-06-12 18:09:51 +00:00
if [$1 = ]; then
echo "no files given"
exit 0
fi
2000-06-12 18:09:51 +00:00
# this for loop iterates through all of the files that we gave the program
# it does one rename per file given
for file in $*
do
mv ${file} $prefix$file
done
2000-06-12 18:09:51 +00:00
#we now exit the program
exit 0
fi
2000-06-12 18:09:51 +00:00
# check for a suffix rename
# the rest of this part is virtually identical to the previous section
# please see those notes
if [ $1 = s ]; then
suffix=$2 ; shift ; shift
2000-06-12 18:09:51 +00:00
if [$1 = ]; then
echo "no files given"
exit 0
fi
2000-06-12 18:09:51 +00:00
for file in $*
do
mv ${file} $file$suffix
done
2000-06-12 18:09:51 +00:00
exit 0
fi
2000-06-12 18:09:51 +00:00
# check for the replacement rename
if [ $1 = r ]; then
2000-06-12 18:09:51 +00:00
shift
2000-06-12 18:09:51 +00:00
# i included this bit as to not damage any files if the user does not specify
# anything to be done
# just a safety measure
2000-06-12 18:09:51 +00:00
if [ $# -lt 3 ] ; then
echo "usage: renna r [expression] [replacement] files... "
exit 0
fi
2000-06-12 18:09:51 +00:00
# remove other information
OLD=$1 ; NEW=$2 ; shift ; shift
2000-06-12 18:09:51 +00:00
# this for loop iterates through all of the files that we give the program
# it does one rename per file given using the program 'sed'
# this is a simple command line program that parses standard input and
2000-06-12 18:09:51 +00:00
# replaces a set expression with a give string
# here we pass it the file name ( as standard input) and replace the nessesary
# text
2000-06-12 18:09:51 +00:00
for file in $*
do
new=`echo ${file} | sed s/${OLD}/${NEW}/g`
mv ${file} $new
done
exit 0
fi
2000-06-12 18:09:51 +00:00
# if we have reached here then nothing proper was passed to the program
# so we tell the user how to use it
echo "usage;"
echo " renna p [prefix] files.."
echo " renna s [suffix] files.."
echo " renna r [expression] [replacement] files.."
exit 0
2000-06-12 18:09:51 +00:00
# done!
2000-06-12 18:09:51 +00:00
</verb></tscreen>
</sect1>
<sect1>
File renamer (simple)
<p>
<tscreen><verb>
#!/bin/bash
# renames.sh
# basic file renamer
criteria=$1
re_match=$2
replace=$3
for i in $( ls *$criteria* );
2000-06-12 18:09:51 +00:00
do
src=$i
tgt=$(echo $i | sed -e "s/$re_match/$replace/")
mv $src $tgt
done
</verb></tscreen>
</sect1>
</sect>
<sect>
When something goes wrong (debugging)
<sect1>
Ways Calling BASH
2000-06-12 18:09:51 +00:00
<p> A nice thing to do is to add on the first line
<tscreen><verb>
#!/bin/bash -x
</verb></tscreen>
<P> This will produce some intresting output information
</sect1>
</sect>
<sect>
About the document
<P> Feel free to make suggestions/corrections, or whatever you think
2000-06-12 18:09:51 +00:00
it would be interesting to see in this document. I'll try to update
it as soon as I can.
<sect1>
(no) warranty
<P> This documents comes with no warranty of any kind.
and all that
2000-06-12 18:09:51 +00:00
</sect1>
<sect1>
2000-07-18 14:15:50 +00:00
Translations
<p> Italian: by William Ghelfi (wizzy at tiscalinet.it)
2000-07-27 13:51:54 +00:00
<htmlurl name="is here" url="http://web.tiscalinet.it/penguin_rules">
<p> French: by Laurent Martelli
2000-07-27 13:51:54 +00:00
<htmlurl name="is missed" url="http://">
<p> Korean: Minseok Park
2000-07-27 13:51:54 +00:00
<htmlurl url="http://kldp.org" name="http://kldp.org">
<p> Korean: Chun Hye Jin
2000-07-27 13:51:54 +00:00
<htmlurl url="" name="unknown">
<p> Spanish: unknow
2000-07-27 13:51:54 +00:00
<htmlurl url="http://www.insflug.org" name="http://www.insflug.org">
<p> I guess there are more translations, but I don't have any info of them,
2000-07-27 13:51:54 +00:00
if you have it, please, mail it to me so I update this section.
2000-07-18 14:15:50 +00:00
</sect1>
<sect1>
2000-06-12 18:09:51 +00:00
Thanks to
<P>
<itemize>
2000-07-18 14:15:50 +00:00
<item> People who translated this document to other languages (previous section).
2000-06-22 18:54:39 +00:00
<item> Nathan Hurst for sending a lot of corrections.
<item> Jon Abbott for sending comments about evaluating arithmetic expressions.
<item> Felix Hudson for writing the <it/renna/ script
<item> Kees van den Broek
2019-04-01 16:24:13 +00:00
(for sending many corrections, re-writting useful commands section)
2000-06-22 18:54:39 +00:00
<item> Mike (pink) made some suggestions about locating bash and testing files
<item> Fiesh make a nice suggestion for the loops section.
2000-07-05 17:23:44 +00:00
<item> Lion suggested to mention a common error (./hello.sh: Command not found.)
2019-04-01 16:25:21 +00:00
<item> Andreas Beck made several corrections and comments.
2000-06-12 18:09:51 +00:00
</itemize>
</sect1>
2000-06-20 14:41:52 +00:00
<sect1>
History
2019-04-01 16:25:21 +00:00
<p> New translations included and minor corrections.
2019-04-01 16:24:13 +00:00
<p> Added the section useful commands re-writen by Kess.
2000-07-05 17:23:44 +00:00
<p> More corrections and suggestions incorporated.
2000-06-22 18:54:39 +00:00
<p> Samples added on string comparison.
2019-04-01 16:25:21 +00:00
<p> v0.8 droped the versioning, I guess the date is enough.
2000-06-22 18:54:39 +00:00
<p> v0.7 More corrections and some old TO-DO sections written.
<p> v0.6 Minor corrections.
<p> v0.5 Added the redirection section.
2019-04-01 16:25:21 +00:00
<p> v0.4 disappeared from its location due to my ex-boss and this doc found its new place
2000-06-22 18:54:39 +00:00
at the proper url: www.linuxdoc.org.
2000-06-20 14:41:52 +00:00
<p> prior: I don't rememeber and I didn't use rcs nor cvs :(
</sect1>
2000-06-12 18:09:51 +00:00
<sect1>
More resources
<P>
<P> Introduction to bash (under BE)
2000-06-20 14:41:52 +00:00
<htmlurl url="http://org.laol.net/lamug/beforever/bashtut.htm"
name="http://org.laol.net/lamug/beforever/bashtut.htm">
<htmlurl url="https://web.archive.org/web/20031203073810/http://org.laol.net/lamug/beforever/bashtut.htm"
name="Link defunct, but here's a link from the Wayback Machine">
<P> Bourne Shell Programming
http://207.213.123.70/book/
2000-06-12 18:09:51 +00:00
</sect1>
</sect>
</article>