ops102:bash_scripting
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
ops102:bash_scripting [2024/03/06 17:18] – chris | ops102:bash_scripting [2024/04/16 18:10] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 64: | Line 64: | ||
Hello World | Hello World | ||
- | === Word Splitting and Quoting | + | ==== Quoting ==== |
+ | === Word Splitting and Quoting === | ||
Spaces and tabs are used to split a bash command line into individual " | Spaces and tabs are used to split a bash command line into individual " | ||
Line 89: | Line 90: | ||
Notice that only one file was created by the command '' | Notice that only one file was created by the command '' | ||
- | You can quote text with single-quotes ['] or double-quotes ["]. Variable expansion takes place inside double quotes, but not inside single quotes: | + | You'll also need to use quoting when assigning a string variable value containing a space: |
+ | |||
+ | $ A=" | ||
+ | |||
+ | You can quote text with single-quotes ['] or double-quotes ["]. Variable expansion takes place inside double quotes | ||
$ B=World | $ B=World | ||
Line 96: | Line 101: | ||
$ echo 'Hello $B' | $ echo 'Hello $B' | ||
Hello $B | Hello $B | ||
+ | |||
+ | You should always double-quote variables that may contain a space in their value when using them as command arguments. | ||
+ | |||
+ | This is especially true for filenames -- you never know when a user is going to put a space in a filename! Many scripts work find with opaque filenames (those containing no whitespace) but fail with non-opaque names. | ||
+ | |||
+ | === Backslashes === | ||
+ | |||
+ | A backslash [\] character outside of quotes or inside double quotes instructs the shell to ignore any special meaning that the following character may have. Examples: | ||
+ | |||
+ | $ touch "new file" | ||
+ | $ ls -l new\ file # The space loses its special meaning as an argument separator | ||
+ | -rw-r--r--. 1 chris chris 0 Jun 18 22:49 'new file' | ||
+ | | ||
+ | $ echo "This string contains a \" | ||
+ | This string contains a " | ||
+ | | ||
+ | $ A=Testing | ||
+ | $ echo " | ||
+ | $A | ||
+ | |||
+ | ===== Environment Variables ===== | ||
+ | |||
+ | By default, a variable is local to the shell in which it is running. | ||
+ | |||
+ | You can //export// variables to make them // | ||
+ | |||
+ | Environment variables are used by all proccesses, not just the shell, and are commonly used to pass configuration information to programs. | ||
+ | |||
+ | To create an environment variable named X with a value of 999: | ||
+ | |||
+ | $ X=999 | ||
+ | $ export X | ||
+ | |||
+ | Or in a single step: | ||
+ | |||
+ | $ export X=999 | ||
+ | |||
+ | You can view all of the current variables (and shell functions) with the '' | ||
+ | |||
+ | The term // | ||
+ | |||
+ | ==== Common Environment Variables ==== | ||
+ | |||
+ | ^ Environment Variable ^ Purpose ^ Examples ^ | ||
+ | | PS1 | Normal (first-level) shell prompt | '' | ||
+ | | EDITOR | Path to the default text editor (typically / | ||
+ | | PATH | A colon-separated list of directories that will be searched when looking for a command | '' | ||
+ | | LANG | The default language -- used to select message translations as well as number, currency, date, and calendar formats. | '' | ||
+ | | HOME | The user's home directory - used for relative-to-home pathnames. | '' | ||
+ | | RANDOM | A random integer (0-32767) | | | ||
+ | |||
+ | ===== Reading Variable Values from Stdin: read ===== | ||
+ | |||
+ | The '' | ||
+ | |||
+ | The '' | ||
+ | |||
+ | $ read -p "Enter your name: " NAME | ||
+ | Enter your name: Chris | ||
+ | $ echo $NAME | ||
+ | Chris | ||
+ | |||
+ | Here is a script which uses a couple of '' | ||
+ | |||
+ | # | ||
+ | read -p " | ||
+ | echo " | ||
+ | read -p " | ||
+ | echo " | ||
+ | echo " | ||
+ | |||
+ | ===== Command Capture ===== | ||
+ | |||
+ | You can capture the output (stdout) of a command as a string using the notation '' | ||
+ | |||
+ | $ echo "The current date and time is: $(date)" | ||
+ | The current date and time is: Mon 19 Jun 2034 12:02:11 AM EDT | ||
+ | |||
+ | $ FILES=" | ||
+ | $ echo "There are $FILES files in the current directory $(pwd)" | ||
+ | There are 2938 files in the current directory /bin | ||
+ | |||
+ | **Avoid Backticks!** You may see old scripts that use backticks (reverse single quotes) for command capture, like this: | ||
+ | |||
+ | $ A=`ls` | ||
+ | |||
+ | This is an archaic syntax with is depricated -- avoid doing this. Some fonts make it hard to distiguish between backticks and single-quotes, | ||
+ | |||
+ | ===== Arithmetic ===== | ||
+ | |||
+ | Bash can perform __integer__ arithmetic. | ||
+ | |||
+ | To evaluate an arithmetic expression and return a value, use ''< | ||
+ | |||
+ | $ A=100 | ||
+ | $ B=12 | ||
+ | $ echo $((A*B)) | ||
+ | 1200 | ||
+ | $ echo $((B++)) | ||
+ | 12 | ||
+ | $ echo $B | ||
+ | |||
+ | Note that inside the double-parenthesis, | ||
+ | |||
+ | To evaluate an arithmetic expression without returning a value, use ''< | ||
+ | |||
+ | $ A=100 | ||
+ | $ B=13 | ||
+ | $ ((A++)) | ||
+ | $ echo $A | ||
+ | 101 | ||
+ | $ ((C=A*B*2)) | ||
+ | $ echo "The answer is $C' | ||
+ | The answer is 2626 | ||
+ | |||
+ | ===== Exit Status Codes ===== | ||
+ | |||
+ | When any process finishes executing, it exits with a numeric value. This can be called the exit code, status code, exit status code, or error code. | ||
+ | |||
+ | Usually, an exit status code of zero (0) means that no errors were encountered, | ||
+ | |||
+ | Be aware that program authors can use this value as they see fit, so the status code may indicate something else, such as the number of data items processed. | ||
+ | |||
+ | The special variable '' | ||
+ | |||
+ | For example: | ||
+ | |||
+ | $ ls -d /etc | ||
+ | /etc | ||
+ | $ echo $? | ||
+ | 0 | ||
+ | | ||
+ | $ ls -d / | ||
+ | ls: cannot access '/ | ||
+ | $ echo $? | ||
+ | 2 | ||
+ | |||
+ | Why is this important? Because exit status codes are the key to conditional logic (if...) and looping (for/ | ||
+ | |||
+ | ===== Conditional Logic: if / then / elif / else / fi ===== | ||
+ | |||
+ | Bash provides an '' | ||
+ | |||
+ | if LIST1 | ||
+ | then | ||
+ | LIST2 | ||
+ | fi | ||
+ | |||
+ | If the command or commands in LIST1 execute successfully and return an exit status code of 0, then the commands in LIST2 are executed. | ||
+ | |||
+ | For example, you could use a '' | ||
+ | |||
+ | if grep -q " | ||
+ | then | ||
+ | echo "The course code ' | ||
+ | fi | ||
+ | |||
+ | The '' | ||
+ | |||
+ | if grep -q " | ||
+ | then | ||
+ | echo "The course code ' | ||
+ | else | ||
+ | echo "The course code ' | ||
+ | fi | ||
+ | |||
+ | It also supports the '' | ||
+ | |||
+ | if grep -q " | ||
+ | then | ||
+ | echo "The course code ' | ||
+ | elif grep -q " | ||
+ | echo "The course code ' | ||
+ | else | ||
+ | echo " | ||
+ | fi | ||
+ | |||
+ | Putting this all together, you could have a script like this: | ||
+ | |||
+ | # | ||
+ | | ||
+ | read -p "Enter a filename: " FILE | ||
+ | | ||
+ | if grep -q " | ||
+ | then | ||
+ | echo "The course code ' | ||
+ | elif grep -q " | ||
+ | echo "The course code ' | ||
+ | else | ||
+ | echo " | ||
+ | fi | ||
+ | |||
+ | ==== The test Command ==== | ||
+ | |||
+ | To perform tests, such as comparisons, | ||
+ | |||
+ | Test accepts parameters that comprise a test, such as a test for string equality, and returns a status code of 0 if the test succeeds or non-0 if it fails: | ||
+ | |||
+ | test " | ||
+ | |||
+ | This is used with the '' | ||
+ | |||
+ | if test " | ||
+ | then | ||
+ | SUPERPOWERS=" | ||
+ | fi | ||
+ | |||
+ | However, this syntax is a bit ugly! So bash provides a synonym for '' | ||
+ | |||
+ | if [[ " | ||
+ | then | ||
+ | SUPERPOWERS=" | ||
+ | fi | ||
+ | |||
+ | Remember that the double square-brackets are really just the '' | ||
+ | |||
+ | **Note:** The original test command in the original Unix shell (the Bourne shell) was aliased to the single square bracket '' | ||
+ | |||
+ | === Available Tests === | ||
+ | |||
+ | There are four main types of tests available: | ||
+ | |||
+ | == Tests Group 1: Filesystem Entries == | ||
+ | |||
+ | These tests check a filename to see if it is a regular file, a directory, or a symbolic link: | ||
+ | |||
+ | [[ -f filename ]] # True if filename is a regular file | ||
+ | [[ -d filename ]] # True if filename is a directory | ||
+ | [[ -l filename ]] # True if filename is a symbolic link | ||
+ | |||
+ | **Note:** If a symbolic link points to a file, then both the '' | ||
+ | |||
+ | == Tests Group 2: File Permissions == | ||
+ | |||
+ | These tests check a filename to see if the person running the script can read, write, or execute the file (or access the directory): | ||
+ | |||
+ | [[ -r filename ]] # True if filename is readable | ||
+ | [[ -w filename ]] # True if filename is writable | ||
+ | [[ -x filename ]] # True if filename is executable (accessible if a directory) | ||
+ | |||
+ | == Tests Group 3: Strings == | ||
+ | |||
+ | These tests accept two string arguments, which are compared: | ||
+ | |||
+ | [[ string1 == string2 ]] # True if the strings are equal | ||
+ | [[ string1 != string2 ]] # True if the strings are not equal | ||
+ | [[ string1 > string2 ]] # True if string1 sorts after string2 lexicographically | ||
+ | [[ string1 < string2 ]] # True if string1 sorts before string2 lexicographically | ||
+ | |||
+ | **A note on the term // | ||
+ | |||
+ | When sorting lexicographically, | ||
+ | |||
+ | a | ||
+ | aa | ||
+ | b | ||
+ | |||
+ | In a similar way, 1 comes before 11, which comes before 2: | ||
+ | |||
+ | 1 | ||
+ | 11 | ||
+ | 2 | ||
+ | |||
+ | Note that this is different from numeric order, where 2 would preceed 11: | ||
+ | |||
+ | 1 | ||
+ | 2 | ||
+ | 11 | ||
+ | |||
+ | == Test Group 4: Integers == | ||
+ | |||
+ | These tests accept to integer arguments, which are compared: | ||
+ | |||
+ | [[ integer1 -eq integer2 ]] # Integers are equal | ||
+ | [[ integer1 -ne integer2 ]] # Integers are not equal | ||
+ | [[ integer1 -gt integer2 ]] # Integer1 is greater than integer2 | ||
+ | [[ integer1 -ge integer2 ]] # Integer1 is greater than or equal to integer2 | ||
+ | [[ integer1 -lt integer2 ]] # Integer1 is less than integer2 | ||
+ | [[ integer1 -le integer2 ]] # Integer1 is less than or equal to integer2 | ||
+ | |||
+ | == Other Tests == | ||
+ | |||
+ | The four groups of tests above will cover the vast majority of situations. There are additional tests available to test other conditions, such as whether a variable is defined, or a file refers to a device. See the man page for bash(1) for more information if you're interested in other tests: '' | ||
+ | |||
+ | == Negating and Combining Tests == | ||
+ | |||
+ | The '' | ||
+ | |||
+ | [[ ! -f " | ||
+ | |||
+ | The AND operator ''&&'' | ||
+ | |||
+ | [[ $A -lt $B && $B -lt $C ]] # True if A is less than B, and also B is less than C. | ||
+ | |||
+ | The OR operator '' | ||
+ | |||
+ | [[ $A -gt $B || $A -lt 0 ]] # True if either: A is greater than B, or if A is less than 0. | ||
+ | |||
+ | You can use multiple '' | ||
+ | |||
+ | [[ -f " | ||
+ | |||
+ | == Tips on Using Tests == | ||
+ | |||
+ | * Remember to quote any arguments which include whitespace, or which may be null (empty). | ||
+ | * Be careful with the ''<'' | ||
+ | |||
+ | ==== Parameters ==== | ||
+ | |||
+ | Arguments to a script are called parameters. You can access the parameters using the special variables '' | ||
+ | |||
+ | The special variable '' | ||
+ | |||
+ | Here is a simple script which shows you what parameters have been received: | ||
+ | |||
+ | # | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | |||
+ | When you run this script with three parameters (red, green, and blue), you get this output: | ||
+ | |||
+ | $ ./params red green blue | ||
+ | Number of parameters: | ||
+ | Parameter 0: | ||
+ | Parameter 1: | ||
+ | Parameter 2: | ||
+ | Parameter 3: | ||
+ | Parameter 4: | ||
+ | |||
+ | The '' | ||
+ | |||
+ | $ cat params2 | ||
+ | # | ||
+ | echo " | ||
+ | | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | | ||
+ | echo "---- Performing shift ----" | ||
+ | shift | ||
+ | | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | echo " | ||
+ | | ||
+ | $ ./params2 red green blue | ||
+ | Number of parameters: | ||
+ | Parameter 0: | ||
+ | Parameter 1: red | ||
+ | Parameter 2: green | ||
+ | Parameter 3: blue | ||
+ | Parameter 4: | ||
+ | ---- Performing shift ---- | ||
+ | Parameter 0: | ||
+ | Parameter 1: green | ||
+ | Parameter 2: blue | ||
+ | Parameter 3: | ||
+ | Parameter 4: | ||
+ | |||
+ | The '' | ||
+ | |||
+ | ==== Example Scripts ==== | ||
+ | |||
+ | === Computer Architecture === | ||
+ | |||
+ | This script displays a message based on the architecture of the computer: | ||
+ | |||
+ | # | ||
+ | architecture=" | ||
+ | | ||
+ | if [[ " | ||
+ | then | ||
+ | echo "Your computer architecture is Intel/AMD x86_64." | ||
+ | elif [[ " | ||
+ | then | ||
+ | echo "Your computer uses the 64-bit Arm architecture." | ||
+ | else | ||
+ | echo "Your computer uses an unrecognized architecture." | ||
+ | fi | ||
+ | |||
+ | === Age Check === | ||
+ | |||
+ | This script checks whether a customer is of legal drinking age in Ontario: | ||
+ | |||
+ | # | ||
+ | read -p "Enter the customer' | ||
+ | | ||
+ | # Calculate the time in seconds that the customer turns/tuned 19 | ||
+ | D=" | ||
+ | | ||
+ | # Get the current time in seconds | ||
+ | NOW=" | ||
+ | | ||
+ | # Tell the user if the customer is old enough to be served alcohol | ||
+ | # This tests checks to see if the customer' | ||
+ | # less than (before) the current date. | ||
+ | if [[ " | ||
+ | then | ||
+ | echo "The customer is of legal drinking age in Ontario." | ||
+ | else | ||
+ | echo "The customer is too young to legally drink in Ontario." | ||
+ | fi | ||
+ | |||
+ | === Coinflip === | ||
+ | |||
+ | This script flips a virtual coin: | ||
+ | |||
+ | # | ||
+ | | ||
+ | COINFLIP=$((RANDOM % 2)) | ||
+ | if [[ " | ||
+ | then | ||
+ | echo " | ||
+ | else | ||
+ | echo "Tails 😦" | ||
+ | fi | ||
+ | |||
+ | The COINFLIP variable is set to the remainder of the division of '' | ||
+ | |||
+ | Note that this script uses extended Unicode characters -- however, for these to display properly, your terminal and your terminal font must both support the extended characters. Besides emoji, extended characters may be used to display accented characters, symbols, and characters from other languages. | ||
+ | |||
+ | === Cautious File Delete === | ||
+ | |||
+ | This script checks a file, provided as a positional argument (parameter), | ||
+ | |||
+ | # | ||
+ | if [[ " | ||
+ | then | ||
+ | echo " | ||
+ | exit 1 | ||
+ | fi | ||
+ | F=" | ||
+ | if [ ! -f " | ||
+ | then | ||
+ | echo "The filename ' | ||
+ | elif [ ! -w " | ||
+ | then | ||
+ | echo "The file ' | ||
+ | else | ||
+ | read -p " | ||
+ | if [[ " | ||
+ | || " | ||
+ | then | ||
+ | echo " | ||
+ | rm " | ||
+ | echo " | ||
+ | else | ||
+ | echo " | ||
+ | fi | ||
+ | fi | ||
ops102/bash_scripting.1709745538.txt.gz · Last modified: 2024/04/16 18:10 (external edit)