105.1 Lesson 3
Certificate: |
LPIC-1 |
---|---|
Version: |
5.0 |
Topic: |
105 Shells and Shell Scripting |
Objective: |
105.1 Customize and use the shell environment |
Lesson: |
3 of 3 |
Introduction
After going through shells, startup scripts and variables in the previous lessons, we will round the whole topic of customizing the shell off by having a look at two very interesting shell elements: aliases and functions. In fact the entire group of variables, aliases and functions — and their influence on one another — is what makes up the shell environment.
The main strength of these two flexible and time-saving shell facilities has to do with the concept of encapsulation: they offer the possibility of putting together — under a single command — a series of repetitive or recurrent commands.
Creating Aliases
An alias is a substitute name for another command(s). It can run like a regular command, but instead executes another command according to the alias definition.
The syntax for declaring aliases is quite straightforward. Aliases are declared by writing the keyword alias
followed by the alias assignment. In turn, the alias assignment consists of the alias name, an equal sign and one or more commands:
alias alias_name=command(s)
For example:
$ alias oldshell=sh
This awkward alias will start an instance of the original sh
shell when the user types oldshell
into the terminal:
$ oldshell $
The power of aliases lies in that they allow us to write short versions of long commands:
$ alias ls='ls --color=auto'
Note
|
For information about |
Likewise, we can create aliases for a series of concatenated commands — the semicolon (;
) is used as a delimiter. We can, for instance, have an alias that gives us information about the location of the git
executable and its version:
$ alias git_info='which git;git --version'
To invoke an alias, we type its name into the terminal:
$ git_info /usr/bin/git git version 2.7.4
The alias
command will produce a listing of all available aliases in the system:
$ alias alias git-info='which git;git --version' alias ls='ls --color=auto' alias oldshell='sh'
The unalias
command removes aliases. We can, for instance, unalias git-info
and see how it disappears from the listing:
$ unalias git-info $ alias alias ls='ls --color=auto' alias oldshell='sh'
As we saw with the alias hi='echo We salute you.'
in a previous lesson, we must enclose commands in quotes (either single or double) when — as a result of having arguments or parameters — they contain spaces:
$ alias greet='echo Hello world!' $ greet Hello world!
Commands with spaces include also those with options:
$ alias ll='ls -al'
Now ll
will list all files — including the hidden ones (a
) — in the long format (l
).
We can reference variables in aliases:
$ reptile=uromastyx $ alias greet='echo Hello $reptile!' $ greet Hello uromastyx!
The variable can also be assigned within the alias:
$ alias greet='reptile=tortoise; echo Hello $reptile!' $ greet Hello tortoise!
We can escape an alias with \
:
$ alias where?='echo $PWD' $ where? /home/user2 $ \where? -bash: where?: command not found
Escaping an alias is useful when an alias has the same name as a regular command. In this case, the alias takes precedence over the original command, which, however, is still be accessible by escaping the alias.
Likewise, we can put an alias inside another alias:
$ where? /home/user2 $ alias my_home=where? $ my_home /home/user2
On top of that, we can also put a function inside an alias as you will be shown below.
Expansion and Evaluation of Quotes in Aliases
When using quotes with environment variables, single quotes make the expansion dynamic:
$ alias where?='echo $PWD' $ where? /home/user2 $ cd Music $ where? /home/user2/Music
However, with double quotes the expansion is done statically:
$ alias where?="echo $PWD" $ where? /home/user2 $ cd Music $ where? /home/user2
Persistence of Aliases: Startup Scripts
Just as with variables, for our aliases to gain persistence, we must put them into initialization scripts that are sourced at startup. As we already know, a good file for users to put their personal aliases in is ~/.bashrc
. You will probably find some aliases there already (most of them commented out and ready to be used by removing the leading #
):
$ grep alias .bashrc # enable color support of ls and also add handy aliases alias ls='ls --color=auto' #alias dir='dir --color= #alias vdir='vdir --color= #alias grep='grep --color= #alias fgrep='fgrep --color' #alias egrep='egrep --color= # some more ls aliases #ll='ls -al' #alias la='ls -A' #alias l='ls -CF' # ~/.bash_aliases, instead of adding them here directly. if [ -f ~/.bash_aliases ]; then . ~/.bash_aliases
As you can read in the last three lines, we are offered the possibility of having our own alias-dedicated file — ~/.bash_aliases
— and have it sourced by .bashrc
with every system start. So we can go for that option and create and populate such file:
########### # .bash_aliases: # a file to be populated by the user's personal aliases (and sourced by ~/.bashrc). ########### alias git_info='which git;git --version' alias greet='echo Hello world!' alias ll='ls -al' alias where?='echo $PWD'
Creating Functions
Compared to aliases, functions are more programmatic and flexible, specially when it comes to exploiting the full potential of Bash special built-in variables and positional parameters. They are also great to work with flow control structures such as loops or conditionals. We can think of a function as a command which includes logic through blocks or collections of other commands.
Two Syntaxes for Creating Functions
There are two valid syntaxes to define functions.
- Using the keyword
function
-
On the one hand, we can use the keyword
function
, followed by the name of the function and the commands between curly brackets:function function_name { command #1 command #2 command #3 . . . command #n }
- Using
()
-
On the other, we can leave out the keyword
function
and use two brackets right after the name of the function instead:function_name() { command #1 command #2 command #3 . . . command #n }
It is commonplace to put functions in files or scripts. However, they can also be written directly into the shell prompt with each command on a different line — note PS2
(>
) indicating a new line after a line break:
$ greet() { > greeting="Hello world!" > echo $greeting > }
Whatever the case — and irrespective of the syntax we choose — , if we decide to skip line breaks and write a function in just one line, commands must be separated by semicolons (note the semicolon after the last command too):
$ greet() { greeting="Hello world!"; echo $greeting; }
bash
did not complain when we pressed Enter, so our function is ready to be invoked. To invoke a function, we must type its name into the terminal:
$ greet Hello world!
Just as with variables and aliases, if we want functions to be persistent across system reboots we have to put them into shell initialization scripts such as /etc/bash.bashrc
(global) or ~/.bashrc
(local).
Warning
|
After adding aliases or functions to any startup script file, you should source such files with either |
Bash Special Built-in Variables
The Bourne Again Shell comes with a set of special variables which are particularly useful for functions and scripts. These are special because they can only be referenced — not assigned. Here is a list of the most relevant ones:
$?
-
This variable’s reference expands to the result of the last command run. A value of
0
means success:$ ps aux | grep bash user2 420 0.0 0.4 21156 5012 pts/0 Ss 17:10 0:00 -bash user2 640 0.0 0.0 12784 936 pts/0 S+ 18:04 0:00 grep bash $ echo $? 0
A value other than
0
means error:user1@debian:~$ ps aux |rep bash -bash: rep: command not found user1@debian:~$ echo $? 127
$$
-
It expands to the shell PID (process ID):
$ ps aux | grep bash user2 420 0.0 0.4 21156 5012 pts/0 Ss 17:10 0:00 -bash user2 640 0.0 0.0 12784 936 pts/0 S+ 18:04 0:00 grep bash $ echo $$ 420
$!
-
It expands to the PID of the last background job:
$ ps aux | grep bash & [1] 663 $ user2 420 0.0 0.4 21156 5012 pts/0 Ss+ 17:10 0:00 -bash user2 663 0.0 0.0 12784 972 pts/0 S 18:08 0:00 grep bash ^C [1]+ Done ps aux | grep bash $ echo $! 663
NoteRemember, the ampersand (
&
) is used to start processes in the background. - Positional parameters
$0
through$9
-
They expand to the parameters or arguments being passed to the function (alias or script) —
$0
expanding to the name of the script or shell.Let us create a function to demonstrate positional parameters — note
PS2
(>
) indicating new lines after line breaks:$ special_vars() { > echo $0 > echo $1 > echo $2 > echo $3 }
Now, we will invoke the function (
special_vars
) passing three parameters to it (debian
,ubuntu
,zorin
):$ special_vars debian ubuntu zorin -bash debian ubuntu zorin
It worked as expected.
WarningAlthough passing positional parameters to aliases is technically possible, it is not at all functional since — with aliases — positional parameters are always passed at the end:
$ alias great_editor='echo $1 is a great text editor' $ great_editor emacs is a great text editor emacs
Other Bash special built-in variables include:
$#
-
It expands to the number of arguments passed to the command.
$@
,$*
-
They expand to the arguments passed to the command.
$_
-
It expands to the last parameter or the name of the script (amongst other things; see
man bash
to find out more!):
Variables in Functions
Of course, variables can be used within functions.
To prove it, this time we will create a new empty file called funed
and put the following function into it:
editors() { editor=emacs echo "My editor is: $editor. $editor is a fun text editor." }
As you may have guessed by now, we must source the file first to be able to invoke the function:
$ . funed
And now we can test it:
$ editors My editor is emacs. emacs is a fun text editor.
As you can appreciate, for the editors
function to work properly, the editor
variable must first be set. The scope of that variable is local to the current shell and we can reference it as long as the session lasts:
$ echo $editor emacs
Together with local variables we can also include environment variables in our function:
editors() { editor=emacs echo "The text editor of $USER is: $editor." } editors
Note how this time we decided to call the function from within the file itself (editors
in the last line). That way, when we source the file, the function will also be invoked — all at once:
$ . funed The text editor of user2 is: emacs.
Positional Parameters in Functions
Something similar occurs with positional parameters.
We can pass them to functions from within the file or script (note the last line: editors tortoise
):
editors() { editor=emacs echo "The text editor of $USER is: $editor." echo "Bash is not a $1 shell." } editors tortoise
We source the file and prove it works:
$ . funed The text editor of user2 is: emacs. Bash is not a tortoise shell.
And we can also pass positional parameters to functions at the command line. To prove it, we get rid of the last line of the file:
editors() { editor=emacs echo "The text editor of $USER is: $editor." echo "Bash is not a $1 shell." }
Then, we have to source the file:
$ . funed
Finally, we invoke the function with tortoise
as the positional parameter $1
at the command line:
$ editors tortoise The text editor of user2 is: emacs. Bash is not a tortoise shell.
Functions in Scripts
Functions are mostly found in Bash scripts.
Turning our funed
file into a script (we will name it funed.sh
) is really a piece of cake:
#!/bin/bash editors() { editor=emacs echo "The text editor of $USER is: $editor." echo "Bash is not a $1 shell." } editors tortoise
That is it! We only added two lines:
-
The first line is the shebang and defines what program is going to interpret the script:
#!/bin/bash
. Curiously enough, that program isbash
itself. -
The last line is simply the invocation of the function.
Now there is only one thing left — we have to make the script executable:
$ chmod +x funed.sh
And now it is ready to be executed:
$ ./funed.sh The text editor of user2 is: emacs. Bash is not a tortoise shell.
Note
|
You will learn all about shell scripting in the next few lessons. |
A Function within an Alias
As said above, we can put a function inside an alias:
$ alias great_editor='gr8_ed() { echo $1 is a great text editor; unset -f gr8_ed; }; gr8_ed'
This long alias value deserves an explanation. Let us break it down:
-
First there is the function itself:
gr8_ed() { echo $1 is a great text editor; unset -f gr8_ed; }
-
The last command in the function —
unset -f gr8_ed
— unsets the function so that it does not remain in the presentbash
session after the alias is called. -
Last but not least, to have a successful alias invocation, we must first invoke the function too:
gr8_ed
.
Let us invoke the alias and prove it works:
$ great_editor emacs emacs is a great text editor
As shown in unset -f gr8_ed
above, the unset
command is not only used to unset variables, but also functions. In fact, there are specific switches or options:
unset -v
-
for variables
unset -f
-
for functions
If used without switches, unset
will try to unset a variable first and — if it fails — then it will try to unset a function.
A Function within a Function
Now say we want to communicate two things to user2
every time she logs into the system:
-
Say hello and recommend/praise a text editor.
-
Since she is starting to put a lot of
Matroska
video files in its$HOME/Video
folder, we also want to give her a warning.
To accomplish that purpose, we have put the following two functions into /home/user2/.bashrc
:
The first function (check_vids
) does the checking on .mkv
files and the warning:
check_vids() { ls -1 ~/Video/*.mkv > /dev/null 2>&1 if [ "$?" = "0" ];then echo -e "Remember, you must not keep more than 5 video files in your Video folder.\nThanks." else echo -e "You do not have any videos in the Video folder. You can keep up to 5.\nThanks." fi }
check_vids
does three things:
-
It lists the
mkv
files in~/Video
sending the output — and any errors — to the so-called bit-bucket (/dev/null
). -
It tests the output of the previous command for success.
-
Depending on the result of the test, it echoes one of two messages.
The second function is a modified version of our editors
function:
editors() { editor=emacs echo "Hi, $USER!" echo "$editor is more than a text editor!" check_vids } editors
It is important to observe two things:
-
The last command of
editors
invokescheck_vids
so both functions get chained: The greeting, praise and the checking and warning are executed in sequence. -
editors
itself is the entry point to the sequence of functions so it is invoked in the last line (editors
).
Now, let us log in as user2
and prove it works:
# su - user2 Hi, user2! emacs is more than a text editor! Remember, you must not keep more than 5 video files in your Video folder. Thanks.
Guided Exercises
-
Complete the table with “Yes” or “No” considering the capabilities of aliases and functions:
Feature Aliases? Functions? Local variables can be used
Environment variables can be used
Can be escaped with
\
Can be recursive
Very productive when used with positional parameters
-
Enter the command that list all aliases in your system:
-
Write an alias named
logg
that lists allogg
files in~/Music
— one per line: -
Invoke the alias to prove it works:
-
Now, modify the alias so that it echoes out the session’s user and a colon before the listing:
-
Invoke it again to prove this new version also works:
-
List all aliases again and check your
logg
alias appears in the listing: -
Remove the alias:
-
Study the columns “Alias Name” and “Aliased Command(s)`” and assign the aliases to their values correctly:
Alias Name Aliased Command(s) Alias Assignment b
bash
bash_info
which bash
+echo "$BASH_VERSION"
kernel_info
uname -r
greet
echo Hi, $USER!
computer
pc=slimbook
+echo My computer is a $pc
-
As
root
, write a function calledmy_fun
in/etc/bash.bashrc
. The function must say hello to the user and tell them what their path is. Invoke it so that the user gets both messages every time they log in: -
Login as
user2
to check it works: -
Write the same function in just one line:
-
Invoke the function:
-
Unset the function:
-
This is a modified version of the
special_vars
function:$ special_vars2() { > echo $# > echo $_ > echo $1 > echo $4 > echo $6 > echo $7 > echo $_ > echo $@ > echo $? > }
This is the command we use to invoke it:
$ special_vars2 crying cockles and mussels alive alive oh
Guess the outputs:
Reference Value echo $#
echo $_
echo $1
echo $4
echo $6
echo $7
echo $_
echo $@
echo $?
-
Based on the sample function (
check_vids
) in the section “A Function within a Function”, write a function namedcheck_music
to include into abash
startup script that accepts positional parameters so that we can modify easily:-
the type of file being checked:
ogg
-
the directory in which files are saved:
~/Music
-
the type of file being kept:
music
-
the number of files being saved:
7
-
Explorational Exercises
-
Read-only functions are those whose contents we cannot modify. Do a research on readonly functions and complete the following table:
Function Name Make it readonly List all readonly Functions my_fun
-
Search the web for how to modify
PS1
and anything else you may need to write a function calledfyi
(to be placed in a startup script) which gives the user the following information:-
name of the user
-
home directory
-
name of the host
-
operating system type
-
search path for executables
-
mail directory
-
how often mail is checked
-
how many shells deep the current session is
-
prompt (you should modify it so that it shows
<user>@<host-date>
)
-
Summary
In this lesson you learned:
-
Both aliases and functions are important features of the shell that allow us to encapsulate recurrent blocks of code.
-
Aliases are useful to have shorter versions of long and/or complicated commands.
-
Functions are procedures that implement logic and allow us to automate tasks, specially when used in scripts.
-
The syntax to write aliases and functions.
-
How to concatenate various commands by means of the semicolon (
;
). -
How to properly use quotes with aliases.
-
How to make aliases and functions persistent.
-
Bash special built-in variables:
$?
,$$
,$!
, positional parameters ($0
-$9
),$#
,$@
,$*
and$_
. -
How to use variables and positional parameters with functions.
-
How to use functions in scripts.
-
How to invoke a function from an alias.
-
How to invoke a function from another function.
-
The basics for creating a
bash
script.
Commands and keywords used in this lesson:
alias
-
Create aliases.
unalias
-
Remove aliases.
cd
-
Change directory.
grep
-
Print lines matching a pattern.
function
-
Shell keyword to create functions.
.
-
Source a file.
source
-
Source a file.
ps
-
Report a snapshot of the current processes.
echo
-
Display a line of text.
chmod
-
Change mode bits of a file, for example make it executable.
unset
-
Unset variables and functions.
su
-
Change user ID or become superuser.
Answers to Guided Exercises
-
Complete the table with “Yes” or “No” considering the capabilities of aliases and functions:
Feature Aliases? Functions? Local variables can be used
Yes
Yes
Environment variables can be used
Yes
Yes
Can be escaped with
\
Yes
No
Can be recursive
Yes
Yes
Very productive when used with positional parameters
No
Yes
-
Enter the command that list all aliases in your system:
alias
-
Write an alias named
logg
that lists allogg
files in~/Music
— one per line:alias logg='ls -1 ~/Music/*ogg'
-
Invoke the alias to prove it works:
logg
-
Now, modify the alias so that it echoes out the session’s user and a colon before the listing:
alias logg='echo $USER:; ls -1 ~/Music/*ogg'
-
Invoke it again to prove this new version also works:
logg
-
List all aliases again and check your
logg
alias appears in the listing:alias
-
Remove the alias:
unalias logg
-
Study the columns “Alias Name” and “Aliased Command(s)” and assign the aliases to their values correctly:
Alias Name Aliased Command(s) Alias Assignment b
bash
alias b=bash
bash_info
which bash
+echo "$BASH_VERSION"
alias bash_info='which bash; echo "$BASH_VERSION"'
kernel_info
uname -r
alias kernel_info='uname -r'
greet
echo Hi, $USER!
alias greet='echo Hi, $USER'
computer
pc=slimbook
+echo My computer is a $pc
alias computer='pc=slimbook; echo My computer is a $pc'
NoteSingle quotes can also be replaced by double ones.
-
As
root
, write a function calledmy_fun
in/etc/bash.bashrc
. The function must say hello to the user and tell them what their path is. Invoke it so that the user gets both messages every time they log in:Option A:
my_fun() { echo Hello, $USER! echo Your path is: $PATH } my_fun
Option B:
function my_fun { echo Hello, $USER! echo Your path is: $PATH } my_fun
-
Login as
user2
to check it works:su - user2
-
Write the same function in just one line:
Option A:
my_fun() { echo "Hello, $USER!"; echo "Your path is: $PATH"; }
Option B:
function my_fun { echo "Hello, $USER!"; echo "Your path is: $PATH"; }
-
Invoke the function:
my_fun
-
Unset the function:
unset -f my_fun
-
This is a modified version of the
special_vars
function:$ special_vars2() { > echo $# > echo $_ > echo $1 > echo $4 > echo $6 > echo $7 > echo $_ > echo $@ > echo $? > }
This is the command we use to invoke it:
$ special_vars2 crying cockles and mussels alive alive oh
Guess the outputs:
Reference Value echo $#
7
echo $_
7
echo $1
crying
echo $4
mussels
echo $6
alive
echo $7
oh
echo $_
oh
echo $@
crying cockles and mussels alive alive oh
echo $?
0
-
Based on the sample function (
check_vids
) in section “A Function within a Function”, write a function namedcheck_music
to include into abash
startup script that accepts positional parameters so that we can modify easily:-
the type of file being checked:
ogg
-
the directory in which files are saved:
~/Music
-
the type of file being kept:
music
-
the number of files being saved:
7
check_music() { ls -1 ~/$1/*.$2 > ~/.mkv.log 2>&1 if [ "$?" = "0" ];then echo -e "Remember, you must not keep more than $3 $4 files in your $1 folder.\nThanks." else echo -e "You do not have any $4 files in the $1 folder. You can keep up to $3.\nThanks." fi } check_music Music ogg 7 music
-
Answers to Explorational Exercises
-
Read-only functions are those whose contents we cannot modify. Do a research on readonly functions and complete the following table:
Function Name
Make it readonly
List all readonly Functions
my_fun
readonly -f my_fun
readonly -f
-
Search the web for how to modify
PS1
and anything else you may need to write a function calledfyi
(to be placed in a startup script) which gives the user the following information:-
name of the user
-
home directory
-
name of the host
-
operating system type
-
search path for executables
-
mail directory
-
how often mail is checked
-
how many shells deep the current session is
-
prompt (you should modify it so that it shows
<user>@<host-date>
)fyi() { echo -e "For your Information:\n Username: $USER Home directory: $HOME Host: $HOSTNAME Operating System: $OSTYPE Path for executable files: $PATH Your mail directory is $MAIL and is searched every $MAILCHECK seconds. The current level of your shell is: $SHLVL" PS1="\u@\h-\d " } fyi
-