Introduction to Shell
Introduction
Section titled “Introduction”The shell is the command interpreter: executes the given commands one by one.
loop forever <accept command from console> <execute command>end loop;shell is a command processor that accept commands given from the terminal (or from a command file) and execute them until the file’s end.
As a convention, inside all the shell snippets written on those blog posts, $ symbol (dollar sign) and a space will precede a command you could launch on your Terminal.
You must not write the $ and the space on your Terminal window, but what’s after it.
I’ll also write the output in the lines right below that command.
Here’s an example:
COMMAND# OUTPUT OF THE COMMAND# my own comments about what's happeningDo not confuse COMMAND with $COMMAND, which is a variable named COMMAND.
^KEY means the key combination Ctrl + KEY. So ^C means Ctrl + C, ^X means Ctrl + X and so on.
Here are some useful key combinations:
^Cstops the executing of a command in the Terminal^Lclears the screen and place the cursor back on top
Moreover, I really suggest you to read UNIX Introduction article before moving on. Anyway, let’s make a very brief recap about wild cards and parsing.
Special characters
Section titled “Special characters”Wild cards are also named metacharacters or special characters. They allow the shell to execute pattern matching between a string and file names in the present working directory.
The symbol * abbreviates any string of zero or more characters in a file name:
[ccc...] stands for any character in a file name, between the elements the square brackets.
[c-c...] stands for any character in a file name, in all the elements between the extremes of the set(s) in the square brackets.
The hash symbol # comments until the end of the line (as you can see in almost all the examples).
The backslash (\ symbol) is the escape metacharacter: it tells bash not to interpret the subsequent character as a special character.
Please Note:
- Current directory is identified by the single dot (
.symbol). - Parent directory of a given directory is identified by a couple of dots (
..).
Parsing
Section titled “Parsing”Before executing, the command line is parsed to look after special characters. The very first parsed metacharacters are redirection and piping. In the subsequent scanning, if the shell find any other special character, result in one of the following substitutions:
- command: commands place among
“(backquotes) are executed and the result is produced, - variable & parameters: variables’ names (
$VAR_NAME) are substituted with their actual values, - file names:
*,?,[]metacharacters are substituted into the file names using pattern matching
Moreover, the use of the quotes (single and double) allow and disallow certain types of substitutions:
- Single quotes disallow all the substitution types
- Double quotes only disallow file names substitutions
Both single and double quotes disallow the shell to interpreter redirection and piping metacharacters.
In addition, eval command executes arguments as a shell command. It allows an additional phases of substitution.
Program the shell
Section titled “Program the shell”The command processor can elaborate commands taking them from a file which is usually called command file.
A commands file can contain:
- passing of parameters
- variables
- control flow statements
Note that what instructions are available depends on what shell is being used.
Please Note: shell, meant as a programming language, is interpreted (not compiled).
Shell types
Section titled “Shell types”In modern Linux (and Unix) systems, there are many types of shells:
- sh: Bourne Shell
- bash: Bourne Again Shell, advanced sh version
- zsh: Z Shell, very advanced sh version
- ksh: Korn Shell
- csh: C Shell, syntax similar to C language
- tcsh: Turbo C Shell, advanced csh version
- rush: Ruby Shell, Ruby-based shell
- hotwire
Hashbang
Section titled “Hashbang”Based on the type of shell is being used, the command file, that below will simply be called script, must have a different hashbang (or shebang).
The hashbang is the first line of the script, it tells the shell to run the given script with the specified command.
It’s not mandatory for the hashbang line to be an executable of a shell.
It can be anything, even the cat command: #!/bin/cat.
Note that it must start with #! (while ordinary comments only starts with #).
Here are some hashbang for the shells above:
#!/bin/sh#!/bin/bash#!/bin/zsh# ...Passing parameters
Section titled “Passing parameters”Arguments are positional variables in the invocation line.
$0 represents the name of the command file with is executing.
$1 represents the 1st argument, $2 represents the 2nd argument and so on.
echo $0It’s possible to slide all arguments from right to left (shifting).
$0 will never be lost.
Important variables
Section titled “Important variables”$* stores the set of positional variables which correspond to the command arguments ($1, $2, …).
$# stores the number of passed arguments (excluding $0).
$? stores the integer value returned by the last executed command.
If $? is equal to 0, the command has been executed correctly. If $? is major than 0 (holds an integer value), an error occurred during command execution.
$$ stores the PID (Process Identifier) of the process which is executing.
Other important variables are: $PATH, $HOME, $UID and $IFS.
$IFS contains the list of characters used as input separators.
The default value is space, tab and new line (\n).
You can modify $IFS when you need to process an input made up by data where spaces and tabs have a very different meaning.
Command Execution
Section titled “Command Execution”To run your shell script, need to make it executable:
chmod +x my-command
# give execute permissions to all# .sh files in the current directorychmod +x *.shThe shell looks for commands to execute only inside the directories specified in PATH environment variable.
Paths are separated by colon sign:
echo $PATH/home/pit/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
# display them in a more readable wayecho $PATH | tr ':' '\n'# /home/pit/.local/bin# /usr/local/sbin# /usr/local/bin# /usr/sbin# /usr/bin# /sbin# /bin# /usr/games# /usr/local/games# /snap/binTo execute a command that is not in a directory listed by PATH variable, you need to specify its absolute path.
If you want to execute a command placed in the current working directory:
./my-commandYou can also invoke a shell to execute the command, it will bypass the hashbang:
# SHELL my-commandsh my-commandbash my-commandzsh my-commandIn this way, you don’t need to use the prefix ./ since the shell does not look for commands in the paths of PATH variable, but simply runs my-command (whose name is passed as command line argument).
Given this short script, named my-script.sh:
#!/bin/cat
pwdecho $0echo $HOMEIf you run it, it will execute cat command over the script:
chmod +x my-script.sh./my-script.sh#!/bin/cat
pwdecho $0echo $HOMEBut if you invoke sh shell to execute the command, it will bypass the cat hashbang:
sh ./my-script.sh./my-script.sh# /home/pitInput & Output
Section titled “Input & Output”To get the input from the keyboard you can use the read command.
read reads a single input line and assign its value to the variables passed as parameters. It consider as input separators those defined in $IFS.
read hello world# Hello, World!# write the above line on the Terminal
echo $hello# Hello,
echo $world# World!
echo $hello $world# Hello, World!To avoid errors while parsing the input, it’s better to launch a read for each variable you need to create.
echo command, as you saw many times, outputs a line of text on the standard output.
You can also redirect the output to a file using > sign.
Data types
Section titled “Data types”Variables
Section titled “Variables”Variables are all strings. You must avoid spaces between = sign during assignment, otherwise the shell won’t understand what you’re doing.
hello="Hello, World!"echo $hello# Hello, World!Arrays
Section titled “Arrays”Bourne Shell (sh) does not support arrays. You can simulate them in this way:
n=1eval arr[$n]=aeval echo \${arr[$n]}a # output
n=2eval arr[$n]=beval echo \${arr[$n]}b # output
n=3eval arr[$n]=ceval echo \${arr[$n]}c # output
n='*'eval echo \${arr[$n]}a b c # outputNote that many other types of shell does support arrays.
Control Structures
Section titled “Control Structures”There are a couple of commands that are mandatory to understand and know how to use.
expr command evaluates arithmetical expressions taking string arguments.
test command is used in almost all control structures.
if <commands-list> then <comands> [else <comands>]fiNote that keywords (do, then, fi, …) must be placed after ; separator or in the line below.
if checks the output value of the last command from <commands-list>.
Here’s a simple example:
#!/bin/sh
x="a"y="a"
# both if statements are equivalent# if [ $x = $y ]if test "$x" = "$y" then echo "equal" else echo "NON equal"fiThe output will always be equal unless you change the values of x and y variables.
case <var> in <pattern-1>) <comands> ;; <pattern-i> | <pattern-j> | <pattern-k>) <comands> ;; <pattern-n>) <comands> ;;esaccase-test.sh contents is:
#!/bin/sh
case $1 in "NULL" ) echo "The input is NULL" ;; S* | s* | Y* | y* ) echo "Approved" ;; *) echo "Default branch: none of the above"esacThe 1st case is given by:
./case-test.sh NULLThe input is NULLThe 2nd case:
./case-test.sh Yes./case-test.sh yellow./case-test.sh Sunglesses./case-test.sh silentApproved # all the above lines gets this outputThe default branch is:
./case-test.sh car./case-test.sh Butter./case-test.sh Null # shell is case sensitiveDefault branch: none of the above# all the above lines gets this outputcheck if a parameter is a number
Section titled “check if a parameter is a number”The script check-int.sh accepts one argument and ignore all the others.
#!/bin/sh
# save 1st argument into $numnum="$1"
# check if $num is (or not) a numeric valuecase $num in *[!0-9]*) echo "$num is not a number"; exit 1 ;; *) echo "$num is a number";;esacThe output is:
./_check-int.sh abc# abc is not a number
./_check-int.sh 123# 123 is a number
./_check-int.sh A123# A123 is not a number
./_check-int.sh 456Z# 456Z is not a numberfor <var> [in <list of strings>]do <comands>doneThe sketch below prints all the elements in the current directory:
#!/bin/bash
for i in *do echo "print: "$idonecmd-args.sh script loops over the command arguments and prints them
echo "positional variables are:"for i in $*do echo $idoneLet’s test it out:
./cmd-args.sh /dev/ hello goodpositional variables are:/dev/hellogoodwhile <commands-list>do <comands>donewhile-test.sh script prints on the standard output numbers from 0 to $1:
#!/bin/bash
a=0while test $a -le $1do echo $a a=`expr $a + 1` # incrementdoneThe output is:
./while-test.sh 30123
# if $1 is not an integer number, throws an error./while-test.sh hello./while-test.sh: line 4: test: hello: integer expression expecteduntil <commands-list>do <comands>doneIt’s a while loop with the inverse of the entry condition.
while-test.sh script prints on the standard output numbers from $1 to 0:
#!/bin/bash
a=$1until test $a -le 0do echo $a a=`expr $a - 1` # decrementdoneThe output is:
./until-test.sh 3321
# if $1 is not an integer number# enter an infinite loop throwing errors./until-test.sh helloexpr: syntax error: unexpected argument ‘1’./until-tests.sh: line 4: test: -le: unary operator expected
# you need to manually stop the script^Cselect
Section titled “select”select <variable> in <list-of-variables>do case <variable> in <pattern-i>) <comands> ;; <pattern-j>) <comands> ;; <pattern-k>) <comands> ;; esacdoneOverride the content of PS3 variable to change the message displayed to the user before the choice.
#!/bin/bash
PS3="Choice: "echo "Where do you live? "
select answer in Rome Paris Copenaghen nonedo case $answer in Rome) echo "Rome is the capital of Italy." ;; Paris) echo "Look the Eiffel Tower." ;; Copenaghen) echo "The most populous city of Denmark." ;; none) break ;; *) echo "You need to enter an item from the list!" ;; esacdoneThe output is:
./select-test.shWhere do you live?1) Rome2) Paris3) Copenaghen4) noneChoice: 1 # user input: 1Rome is the capital of Italy.Choice: 3The most populous city of Denmark.Choice: 2 # user input: 2Look the Eiffel Tower.Choice: 78 # user input: 78You need to enter an item from the list!Choice: 4 # user input: 4, which is the exit caseQuotes
Section titled “Quotes”References: