Based on previous materials by Dr. Robert Kline
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- In your CloudLab experiment, run the following:
~~~bash
sudo su - seed
wget https://cs.wcupa.edu/lngo/assets/src/bash_basics.zip
sudo apt-get update
sudo apt-get install -y unzip
unzip bash_basics.zip
cd bash_basics
ls
~~~
- These scripts will be used to illustrate concepts in the remainder of
this slide deck.
- There is far too much content in the Bash language to be covered in
any single document like this one, a tutorial, or even an introductory
textbook. Inevitably, if you need to write programs in Bash, you will
have to consult the online manual: [https://linux.die.net/man/1/bash](https://linux.die.net/man/1/bash)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
- The file itself must be executable by you.
- If you are the owner of the script you can add that
permission with statements like:
~~~bash
chmod +x SOME-SCRIPT.sh
~~~
or
~~~bash
chmod 700 SOME-SCRIPT.sh
~~~
- The file must either be locatable by its path prefix or have its containing
directory in the PATH variable. A full path to the script might be: `/usr/local/bin/SOME-SCRIPT.sh`
- If the script is in the shell's current directory, this is also a full path: `./SOME-SCRIPT.sh`
- The file must identify itself as self-executing.
- If the first two characters are `#!`, this indicates that the file is a text-based
script file, and that the remaining portion of the first line provides the program to
run the script. Thus, a Bash script begins with this first line: `#!/bin/bash`
- Edit and add `#!/bin/bash` to the first line of `hello.sh`
~~~bash
chmod +x hello.sh
./hello.sh
~~~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
- When a shell is run interactively the lines of a bash program a
re created one-by-one.
- Shell code usually is considers the script to be interactive if the
prompt variable, PS1 is defined, since all statements receive this prompt
before entry.
- In interactive execution, Bash will source each statement, which is a
form of execution in which all variable settings are retained.
- Interactive execution also permits many user-friendly control features not
necessary in script execution such as:
- line repeat control with up and down arrows
- line editing and extension features
- tab-based command and filename completion
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
- The most basic operation on strings is concatenation, which, in Bash, is simply juxtaposition.
In general, whitespace sequences are collapsed into a single blank; whitespace sequences at the
ends of strings are truncated (i.e., trimmed).
- Variables are defined using the assign operator `=` in a very strict sort of way.
- Once a variable, `v`, is defined, its value is automatically used with the expression `$v`.
- A double-quoted variable's value, like `"$y"`, can behave differently from `$y` when the
value has internal whitespace. If there is any doubt, it is recommended to always use **double quotes**.
- A newline is interpreted as a statement terminator. A semicolon (`;`) can also be used as
a statement terminator if you want two or more statements on the same line.
- View, then execute scalars.h
- Observe the corresponding outcomes versus the codes
~~~bash
more scalars.sh
./scalars.sh
~~~
- Type something and hit Enter to exit this script.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
## 4. More about Bash
- Bash, just as other languages, does support additional structured data types in the
form of lists and maps (associative lists).
- It also provides a way of assigning a type to a variable through a the declare
statement. View and execute the following script for observation
~~~bash
more scalar-declares.sh
./scalar-declares.sh
~~~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
## 5. Bash condition
- The bash if-else syntax is unusual compared to other languages.
The format looks like this:
~~~bash
if ...
then
some statements
elif ...
some statements
else
some statements
fi
~~~
The "..." sections represent boolean "tests". The chained `elif` and the `else`
parts are optional. The "then" syntax is often written on the same line as the if
portion like this: `if ...; then`
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
- What is considered as boolean expression in an if test uses this syntax:
~~~bash
if [ BOOLEAN-EXPRESSION ]; then
statements ...
fi
~~~
- The only value regarded as false is the empty string. Bash does not recognize
any numerical types per se, only strings used in a numerical context. An undefined
value is, in every way, equivalent to the empty string in Bash.
- You have to be careful about using an undefined variable in a script since it may
be an exported variable and, thereby, implicitly defined. You can always explicitly
undefined a variable `x` by `unset x`.
- You can verify the values of false by viewing and running this sample script: `falsetest.sh`
~~~bash
more falsetest.sh
./falsetest.sh
~~~
- An example usage is this line in `pingtest.sh`:
~~~bash
[ "$host" ] || { echo usage: $(basename $0) "<host or ip>"; exit 1; }
~~~
- In this example host is the first parameter; if undefined, give a "usage" message.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
- The `if` operator (and other tests) can be used with boolean expressions
using appropriate syntax.
- The test expressions are normally within single brackets [ .. ].
- There is a single space after `[` and before `]`.
- Within these we have these operator usages:
- `=`, `!=`: lexicographic comparison
- `-eq`, `-ne`, `-lt`, `-le`, `-gt`, `-ge`: numerical comparison
- However both double brackets `[[ .. ]]` and double parentheses `(( .. ))`
can serve as delimiters.
- The operators `<` and `>` normally represent *file redirection*,
but can be used for lexicographic comparison, within `[[ .. ]]` and numerical comparison within `(( .. ))`.
- You can view and observe some examples from: `test-values.sh`
~~~bash
more test-values.sh
./test-values.sh
~~~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
- Bash uses primitive globbing patterns for various matching operations.
- The most common is the usage of `*` which matches any sequence of characters.
- Less common is `?` which matches any single character and even less common are
character sets, such as `[A-Z]` and `[^0-9]`.
- These type of expressions stand in contrast to more powerful regular expression
pattern generators which, in Bash, are only available through auxiliary commands.
- Glob patterns are simple, familiar patterns such as those used commonly in file listing:
- `ls *.html` # all HTML files (not starting with ".")
- `ls .??*` # all dot files except "." and ".."
- `ls test[0-3]` # "test0", "test1", "test2", "test3"
- The Bash `case` statement distinguishes itself from an `if/else`
constructions primarily by its ability to test its cases by matching
the argument against glob patterns. The syntax is like this:
~~~bash
case "$file" in
*.txt) # treat "$file" like a text file
;;
*.gif) # treat it like a GIF file
;;
*) # catch-all
;;
esac
~~~
- Unlike C++ or Java syntax, the break exits an enclosing loop, not exit the particular case.
|
Bash has both for and while loops. However, the type of control for these is typically not numerical. The most common looping structure in Bash is the for/in structure like this: for x in … do statements involving $x done
Loops The “…” is a list of things generated in a number of ways. The x is the loop variable which iterates through each item in the list. For example, try running this program in the current directory: $ more fileinfo.sh $ ./fileinfo.sh In this case the things iterated are the files in the current directory. Loops One can use numerical-like looping with the double-parentheses like those in for numerical comparison: for ((i=1; i<=10; ++i)); do echo $i done
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
- The while loop also has an advantage in its ability to read live input.
For example, this simple program reads and echos input lines:
~~~bash
while read line; do
echo "$line"
done
~~~
- In a programmatic setting, it is often useful to process lines generated
from the output of some command.
- Say we want to process all words starting with `my` in the system
dictionary (`/usr/share/dict/words`) by removing - the initial `my` part.
- The following two scripts represent two possible ways of doing so:
~~~bash
more process-lines-1.sh
more process-lines-2.sh
~~~
- The command `grep ^my /usr/share/dict/words` is used to generate the target information.
- The two respective approaches to processing this are:
- input redirection into the `while ... done` loop using the manufactured "input device" `< (grep ^my /usr/share/dict/words)`
- piping (i.e., `|`) the command into the "while ... done" loop.
- It turns out that only the former method works as we want it to. The problem with the
latter method is that the `count` variable is being manipulated in a subshell created by
the pipe operation and so its value cannot be used upon exiting the while loop.
- In contrast, the former method with the odd syntax "<(..)" turns out to be more useful.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
## 7. More Bash
- The Bash language itself has very unintuitive string-processing operations.
Later we'll see how to use UNIX commands to do string processing.
~~~bash
more string-processing.sh
./string-processing.sh
~~~
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
- The Bash language relies heavily on the UNIX-like environment in which it resides in order to create utility scripts. This environment includes many standard UNIX string processing operations such as these:
- `sed`: (stream editor) for regular-expression substitution
- `grep`: can be used to perform match testing with -c (count) option; the -e option uses regular expression instead of glob patterns
- `awk`: captures the fields of a line (separated by whitespace) and does operations on these fields;
- `tr`: translate from one list of characters to another; often used to convert case of a string
- `sed`, `grep`, `awk`, and `tr` are used in Bash via standard I/O. All above operations act on text files when given file name as a parameter, or act from standard input with no arguments.
- A common bash expression which uses an external OPERATION to compute some internal value
looks something like this: `result="$(echo "input string" | OPERATION)"`
- The pipe operator "|" is crucial for passing the input string to OPERATION via echo.
The following program illustrates some of these external operations.
~~~bash
more string-operations.sh
./string-operations.sh
~~~
|