Home | | Linux | | Share This Page |
(double-click any word to see its definition)
#!/bin/bash
echo "Hello, world."
NOTE: Be sure to place a linefeed at the end of your script. Forgetting a terminating linefeed is a common beginner's error.
if [ -e . ]
then
echo "Yes."
else
echo "No."
fi
Run the test script:
$ ./myscript.sh
if test -e .
then
echo "Yes."
else
echo "No."
fi
NOTE: Be sure to read the "test" man page to find out all the different tests that are available:
$ man test
for fn in *; do
echo "$fn"
done
In this example, the "*" is expanded by the shell to a list of all the files in the current directory, then each filename is applied to the loop control area. In such a construct, any whitespace-delimited list will do:
for fn in tom dick harry; do
echo "$fn"
done
$ ./myscript.sh
ls -1 | while read fn; do
echo "$fn"
done
This example uses an option to "ls" (note: the option is "-" followed by the numerical digit "1", not a lowercase "L") that formats file listings with one name per line, then this list is pipelined to a routine that reads lines until there are no more to read. This meets the requirement that linefeeds become the delimiters between list elements, not spaces.
There is plenty more to this topic. Please refer to the list of for more.
n=1
while [ $n -le 6 ]; do
echo $n
let n++
done
$ ./myscript.sh
y=1
while [ $y -le 12 ]; do
x=1
while [ $x -le 12 ]; do
printf "% 4d" $(( $x * $y ))
let x++
done
echo ""
let y++
done
$ ./myscript.sh
1 2 3 4 5 6 7 8 9 10 11 12
2 4 6 8 10 12 14 16 18 20 22 24
3 6 9 12 15 18 21 24 27 30 33 36
4 8 12 16 20 24 28 32 36 40 44 48
5 10 15 20 25 30 35 40 45 50 55 60
6 12 18 24 30 36 42 48 54 60 66 72
7 14 21 28 35 42 49 56 63 70 77 84
8 16 24 32 40 48 56 64 72 80 88 96
9 18 27 36 45 54 63 72 81 90 99 108
10 20 30 40 50 60 70 80 90 100 110 120
11 22 33 44 55 66 77 88 99 110 121 132
12 24 36 48 60 72 84 96 108 120 132 144
PS3="Choose (1-5):"
echo "Choose from the list below."
select name in red green blue yellow magenta
do
break
done
echo "You chose $name."
When run, it looks like this:
$ ./myscript.sh
Choose from the list below.
1) red
2) green
3) blue
4) yellow
5) magenta
Choose (1-5):4
You chose yellow.
As written, this menu code won't catch some kinds of errors (like a number that is out of range). In any application where the user choice must fall into defined bounds, be sure to perform a test on the result before using it. Example:
if [ "$name" = "" ]; then
echo "Error in entry."
exit 1
fi
secretNumber=$(( ((`date +%N` / 1000) % 100) +1 ))
guess=-1
while [ "$guess" != "$secretNumber" ]; do
echo -n "I am thinking of a number between 1 and 100. Enter your guess:"
read guess
if [ "$guess" = "" ]; then
echo "Please enter a number."
elif [ "$guess" = "$secretNumber" ]; then
echo -e "\aYes! $guess is the correct answer!"
elif [ "$secretNumber" -gt "$guess" ]; then
echo "The secret number is larger than your guess. Try again."
else
echo "The secret number is smaller than your guess. Try again."
fi
done
Please study this example carefully, and refer to the reference materials in to understand some of the methods.
array=(red green blue yellow magenta)
len=${#array[*]}
echo "The array has $len members. They are:"
i=0
while [ $i -lt $len ]; do
echo "$i: ${array[$i]}"
let i++
done
Run this example:
$ ./myscript.sh
The array has 5 members. They are:
0: red
1: green
2: blue
3: yellow
4: magenta
Now, before you decide this is a silly, rather useless example, replace one line of the script and run it again:
array=(`ls`)
See what difference this makes (and think of all the kinds of lists you might create for this line).
string="this is a substring test"
substring=${string:10:9}
In this example, the variable "substring" contains the word "substring". Remember this rule:
substring=${string_variable_name:starting_position:length}
The string starting position is zero-based.
alpha="This is a test string in which the word \"test\" is replaced."
beta="${alpha/test/replace}"
The string "beta" now contains an edited version of the original string in which the first case of the word "test" has been replaced by "replace". To replace all cases, not just the first, use this syntax:
beta="${alpha//test/replace}"
Note the double "//" symbol.
Here is an example in which we replace one string with another in a multi-line block of text:
list="cricket frog cat dog"
poem="I wanna be a x\n\
A x is what I'd love to be\n\
If I became a x\n\
How happy I would be.\n"
for critter in $list; do
echo -e ${poem//x/$critter}
done
Run this example:
$ ./myscript.sh
I wanna be a cricket
A cricket is what I'd love to be
If I became a cricket
How happy I would be.
I wanna be a frog
A frog is what I'd love to be
If I became a frog
How happy I would be.
I wanna be a cat
A cat is what I'd love to be
If I became a cat
How happy I would be.
I wanna be a dog
A dog is what I'd love to be
If I became a dog
How happy I would be.
Silly example, huh? It should be obvious that this search & replace capability could have many more useful purposes.$ x="This is my test string." $ echo ${x#* } is my test string.
$ x="This is my test string." $ echo ${x##* } string.
$ x="This is my test string." $ echo ${x% *} This is my test
$ x="This is my test string." $ echo ${x%% *} This
There's lots of information, more than we need. Let's say for the sake of argument that I want the IP of "lo", the loopback interface. I could specify this:$ x=`/sbin/ifconfig` $ echo $x eth0 Link encap:Ethernet HWaddr 00:0D:56:0C:8D:10 inet addr:192.168.0.1 Bcast:192.168.0.255 Mask:255.255.255.0 inet6 addr: fe80::20d:56ff:fe0c:8d10/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:253339 errors:0 dropped:0 overruns:0 frame:0 TX packets:423729 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:36150085 (34.4 MiB) TX bytes:496768499 (473.7 MiB) Base address:0xecc0 Memory:fe4e0000-fe500000 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:109394 errors:0 dropped:0 overruns:0 frame:0 TX packets:109394 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:12372380 (11.7 MiB) TX bytes:12372380 (11.7 MiB)
But, while gobbling text from the left, this search would stop at the IP address of eth0, not the desired interface. So I can specify it this way:$ y=${x#*inet addr:}
As a last step I'll trim off all remaining text to the right:$ y=${x#*lo *inet addr:}
Leaving only the desired address. It seems the "#" and "%" operators, and their variants, are able to accept a rather complex argument and sort through the content of large strings, including strings with line breaks. This means I can use the shell to directly filter content in some simple cases where I might have considered using sed or Perl.$ y=${y%% *}
I have always thought the inability to test for the presence of a string or pattern (without using grep, sed or something similar) was a conspicuous weakness in shell programming. Bash version 3, present on must current Linux distributions, addresses this lack by allowing regular expression matching.
Let's say we need to establish whether variable $x appears to be a social security number:
if [[ $x =~ [0-9]{3}-[0-9]{2}-[0-9]{4} ]] then # process SSN else # print error message fi
Notice the Perlish "=~" syntax and that the regular expression appears within double brackets. A substantial number of regular expression metacharacters are supported, but not some of the Perl-specific extensions like \w, \d and \s.
Another Bash 3 feature is an improved brace expansion operator:
$ echo {a..z} a b c d e f g h i j k l m n o p q r s t u v w x y z
for n in {0..5} do echo $n done 0 1 2 3 4 5
Home | | Linux | | Share This Page |