Linux Consultant

A Simple Bash Tutorial

BASH, or the Bourne Again SHell, is the most popular shell used by Linux and UNIX today. It is a modification of the original System V UNIX ash shell. Also, it is one of the more compatible shells. It is for this reason that I will be using this particular shell. However, most of these commands/syntax will work with most other shells, if not all. Only when we get into scripting will the BASH shell stand out in its uniqueness. This tutorial is mainly aimed at Windows users who would like to get a chance to use a *NIX shell without actually installing a *NIX OS, however the information (excluding the first part) is universal, it doesn't matter if you're using SSH, telnet, or the actual console. Lets get started.

Part I: Telnet to a Linux host if necessary

In Windows, go to START->RUN and type:
telnet
and press enter. This is the Windows version of telnet, a useful tool in all aspects. The telnet application allows a connection to another computer, for the purpose of using that computer as if it were your own. Basically, consider telnet a tool that allows you to get commands on the remote computer sent to you (the output). That is what it is intended for, and all you can use it for in Windows. Now, in Linux, UNIX, etc (referred to as *NIX), we can telnet to any port we decide to, we are not limited at all by only being able to telnet to telnet ports. This is one main reason why *NIX is good for hacking and security audits. *NIX allows telnet to connect to non-telnet ports. However, we will be using telnet for its intended purpose today (aaaawwww), we will be telnetting to a telnet port (23), and connecting to that machine. To do this, we need what is called a SHELL account.

Shell accounts are not usually free, but occasionally you can get a free one. Your ISP might offer one, check with them. If all else fails, these are some good Shell accounts that are FREE:
cjb.cyberspace.net
freeshells.com
deepthought.armory.com
I suggest you at least donating to whichever one you choose, its the right thing to do. Now then, connect to the remote computer by entering in the address. You will usually login as guest, request, etc. to request a free account. Once you have your account set up, go back in a few days or so (some are faster), and again telnet to that remote server, only this time using your login/password.
Okay, now you're connected. this ends the telnet tutorial, now with the BASH tutorial:

Part II: The Shell

Okay, so you have this $ thing. What is this? First of all, the dollar sign you see is a prompt, the remote server is waiting for you do do something. Type the following, and press ENTER:
echo "hello."
Nice, you now can print stuff, kinda useless, eh? not hardly. The beauty of *NIX is that it combines many small programs that just do one thing, so that you can combine them to perform a complex task. Now, lets start with taking a look at what's in our home directory (the path default):
ls -l
the -l specifies to print a long list, showing file permissions, the name, the date, the size, etc. This is similar to the dir command in DOS, but it is more powerful. Okay, now lets edit the following file, either using vi or joe:
vi .bash_profile
OR
joe .bash_profile

This opens the file that is run every time you log in. scroll to the bottom and type :
echo "Welcome to my account."
and press for vi:
ESCAPE
:
write
:
quit

for joe:
Control K + X

and we've exited. Congratulations! You've just edited your first *NIX configuration file, a common, and thus needed skill. You may wish to re-login, or you can simply type the following:
sh .bash_profile
the sh command invokes a shell, which reads the file specified as if it were a script (which it is). We will delve into this later. For now, lets make a listing of our directory, then save it to a file, so we can look at it later:
ls -l > ls_1.txt
What this does is it redirects the output of the command 'ls -l' to a file, which is ls_1.txt. The > redirector does this. Now, lets take a look at that file. We just want to see what's in the file, not edit it, so we'll type:
cat ls_1.txt
If the output is too long, we can use another redirector, the PIPE. This is located usually on the same key as the backspace key. It is a little vertical line. Lets make it to where the output of the 'cat' will go to another program, which is 'more':
cat ls_1.txt | more
this makes it to where you can page through it...nice. Now, then, we shall cover another simple command, the who, or w command (either will work):
w
Will output a list of all logged in users, their task(s), when they logged in, etc. Nice. Lets suppose though that we wanted to use a single command to output the task(s) of our friend, john:
w | grep "john"
as you will remember, the | operator redirects the output of 'w' to 'grep "john"', grep is a tool that searches for text in a file, or whatever input is. For instance, if you wanted to write a file of current users and their tasks, and then search that for john:
w > users.txt
cat users.txt | grep "john"
as you will recall from earlier, cat displays the contents of a file, when piped to grep, grep searches for and displays any strings having john in them. Now then, lets cover another file, .bash_history. .bash_history is exactly that, it is the history of all BASH commands inputted, lets display it:
cat .bash_history
you can pipe it to more if you wish, so you can page down it all. Nice. Now then, lets say that we want to copy the .bash_history file to bash.history.bkup:
cp .bash_history bash.history.bkup
will duplicate the file, so we now have two copies. well, what if we want to rename that file (the bkup) to bash.history:
mv bash.history.bkup bash.history
mv moves files, what we are doing is moving it from one name in your home directory (we'll cover this soon), to the same directory, under a different name. Now then, whats this home directory stuff? As specified in /etc/passwd, your home directory is the directory that you are placed in when you login. You usually have full permissions of this directory, lets see what your home directory is:
echo ${HOME}
The echo command, which we've already covered, prints the ${HOME}, but what is that? That is called a shell variable. $ means that it is a variable, {HOME} specifies that it is the variable named HOME. Let's make a new directory in your home directory, first we'll switch to that directory:
cd ${HOME}
cd means change directory. It changes the user's current directory for that specified. Now, lets make the new dir:
mkdir TEST
mkdir makes a directory, under the name TEST. Now then, lets change directories to that directory:
cd TEST
now we are in that directory. Nice. Now, we'll back out of this one, returning to the parent directory, or the directory above this one:
cd ..
the .. specifies to move back one directory. To verify that we are in your home dir, we will display the current dir:
pwd
displays the present working directory. Okay, now lets copy the backup of our history file to the TEST directory:
cp bash.history TEST
cp once again copies it to a location, this time a directory. cd to that directory, then do an 'ls' to see the contents:
cd TEST
ls -l
the -l part gives a long list, without -l simply shows the file names. Okay, lets create a file, test.txt containing the string 'This is a test.' :
echo "This is a test." > test.txt
Now you can cat it if you wish, but the main reason we created this is to cover the next subject, chmod. 'chmod' Changes a files' permissions, which can be tricky at first, but is *IMPORTANT* for basic security. In order to do this, you need to know binary, or you can use the simpler string format, which we will use in this example, I don't want to confuse anyone this soon, if you want to know how to use chmod, check out the man page (you'll learn about this later, too). Now then, chmod follows the following syntax:
chmod (permissions) (file)
u means the current user
g means the user's group
a means all users
- means take away this privilege
+ means give this privilege
= means that this will be the only permissions this file will have
r means that you can read the file
w means you can write to it
x means that you can execute it
now then, user permissions are displayed as:
-rwxrwxrwx
the first bit, or character is a special one, used to run as that user, to specify a directory, etc, which we won't cover. The next three characters 'rwx' refer to the user that owns that file. The next three after that 'rwx' refer to the permissions of that owner's group. The last three characters 'rwx' specify all other user's permissions. a '-' instead of a character means that that permission is disabled. For instance:
-rwx-r--r--
Means that the owner can read, write, and execute, but the owner's group, and all other users can only read from it. Now then, lets modify our new file, test.txt so that we cannot read from it (follow me here, this is to demonstrate something):
chmod u-r test.txt
Now lets try to cat it:
cat test.txt
You should get an error basically stating that you don't have read permissions for this file. Now, lets change it back:
chmod u+r
and cat it again to be sure that it is changed. Nice, now lets make it to where only we can read, write, and execute to that file:
chmod ga-rwx test.txt
will make it to where no one can read,write,execute:
chmod u+rwx
will give us read, write, and execute privileges. Take this time to play around with it, learn how to use it better.
Now, since we are discussing file ownerships and all, I figured that I would throw in another command, chown. Chown changes a file's owner, following the format of 'chown (user:group) (file):
chown root:root test.txt
Will make it to where only root is the owner. Don't do this, otherwise you will have to write a letter to root, kindly asking them to change the permissions back. For all purposes to you, you should never need to use chown.
Another command you should learn how to use is rm. use this with caution, however. 'rm' removes files:
rm -R ${HOME}/TEST
will completely delete the contents of the directory TEST that we created earlier, plus delete the directory itself. The -R parameter means that it should operate recursively, which means to delete every file, to descend into directories, then remove those files, etc, etc.
Now, lets say that you want to cat a user's manual, but you want to be able to scroll back a page or two wile reading (be able to go back and forth), you would:
cat filename.txt | less
and use the space bar to go forward, and the P key to go to the previous page.

PART III. BASIC SCRIPTING

Now that we have covered the basic commands, we will dive into scripting via the shell. For those of you that feel as if the shell doesn't offer enough power, I recommend you taking a look at Perl, an excellent scripting language that was designed out of a need for more power than the shell provided. One of the most important commands to remember is chmod. When dealing with scripts, you have to make the file executable. This can be accomplished by simply:
chmod u+x filename.sh
OR:
chmod 700 filename.sh
the 700 part will give the owner read write and execute privilege, while giving all other users no privileges whatsoever.
Alternatively, and also more annoyingly, you can simply use the 'sh' command, followed by the script name, without needing to modify the permissions. Lets get into basic principals now of scripting, so we'll start with a simple "hello world" script. Scripts are like normal text files, so you can use a text editor like vi, vim, joe, jed, etc. So, lets create a file called hello.sh, and place in it the following lines:
# hello.sh a simple script
echo "Hello World"
And save it. Now, you can either chmod u+x this file, or you can execute it by:
sh hello.sh
However, if you chmoded it, like I suggest, you will need to:
./hello.sh
Now, whats this './' stuff all about?? The answer is simple, the './' indicator means the current directory, which is similar to '..' when we were playing with 'cd'. If you 'cd ./' it will stay in the same directory. For instance, if the current directory was '/home/user', the value of ./ would be the same as typing /home/user. If you want to just execute hello.sh by just typing hello.sh, you will need to change your search path. Your search path is a variable that is defined as the paths to search for an executable file. Instances of this are '/bin', '/usr/bin', and usually '/usr/sbin'.
Now that we have the actual execution parts taken care of, lets dissect the script. We shall start with the first line, which begins with a '#'. This is known in the UNIX world as a hash mark, and it is used for comments. Anything after a '#' is ignored, so you can type whatever comments you want without any worry of bothering the script. Now, for line 2, echo "Hello World". This command, echo, tells the shell to repeat, or ECHO whatever is in the parentheses, which in this case is Hello World. Now then, lets cover another important feature of scripts, the ability to force many commands onto one line:
# force.sh This demonstrates the use of the semicolon to force many commands onto a single line.
echo "1" ; sleep 1 ; echo "2" sleep 1 ; echo "3"

This should echo 1, wait a second, echo 2, wait another second, then echo 3 to display a nice little timer. First lets cover the sleep command, which follows the basic syntax of sleep is:
sleep {# of seconds} {suffix}
the suffix may be s for seconds (default), m for minutes, h for hours, or d for days. This is used in between commands that you would want a pause in, such as the timer example. Now, lets cover the purpose of this example, the semicolon ';'. The semicolon is used to separate commands that you want executed one after another. Now, you may question the importance of this, so I'll tell it to you: the semicolon is important, because you can enter entire scripts (if they're short enough) on a single command line, without having to make an actual script. So in the example above, with the exception of the first line (everything behind a hash mark is ignored, remember?), if it were entered into a command prompt, it would work just the same. Plus, if you want to get really anal-retentive about it, it would TECHNICALLY be faster, as the system doesn't have to read from the HDD, it is right in memory (the commands). Now then, lets deal with making a script that will echo whatever you want it to:
# argument.sh Echos an argument of the shell script
echo $1

This will echo whatever you choose, you just have to use it in its own special syntax, which is: argument.sh The text that you want displayed.
now then, you may notice that if you type:
./argument.sh text that i want
it only echos text. How to remedy this? You remember that echo had to have quotes surrounding it? Well the same aspect is here, you *have* to enclose it in quotes, like:
./argument.sh "text that I want"
A few other important things you should know:
the & symbol after a command/script tells it to run in the background. For instance, 'ls &' will perform an ls, just in the background.
If you want to re-direct the output of a program to somewhere that it isn't taking up space, in the background, etc, you can redirect it to /dev/null, which will not return any output at all, it is a system device that is designed for uses such as this, for example:
ls > /dev/null
redirects the ls output to /dev/null. Also, you can spawn another shell to execute a command (has to be a command, or executable file, cant be the name of a script in the local dir, w/o the absolute path ('./'), is to use the ! symbol. For instance:
! ls
however, it should be also noted that 'sh' can only execute scripts, not commands (binary executables).
Now then, lets go a step beyond the "hello world" class of scripts, and lets dive into utilities, how to tailor UNIX to your liking.
# delete.sh Moves files to /usr/username/trash, which is only readable by that user.
mv -f $1 ${HOME}/trash
As you will remember, the 'mv' command moves a file, and the -f option forces it. What this does, is it creates a "Recycle Bin" like that in Windows, in your home directory, under trash. Now then, what about recovering it????

# undelete.sh undeletes file specified by user.
mv ${HOME}/trash/${1} ./

This is relatively simple: the mv command of course moves it, this time we'll leave the -f option off, we want to be warned if a file is going to be removed. ${HOME} means $home, or the user's home directory. ALWAYS enclose variables in {}, this keeps the values intact, so it doesn't mess up, without this, the above script(s) won't work. Now then, the ${1} is a little new here, once again enclosed in {}. It is good practice to always enclose your variables in {}, but you only have to if they are in a path, etc. And of course ./ is the current directory. Simple stuff. Now then, lets say that you wanted to time a script to get a directory of all the files in your home directory, then to compare that listing every hour and look for changes:
# ls.sh Compares a directory's contents (in this example the users home dir) for changes in a given time interval, in this case one hour.
ls -l ${HOME} > ${HOME}/.ls
sleep 1h
ls -l ${HOME} > ${HOME}/.ls-comp
diff ${HOME}/.ls ${HOME}/.ls-comp

Will give a non-null output if the contents are changed. Actually, it should have the files listed that have changed as well, but lets break this down some. First line is easy, it executes an ls -l (long list of directory) in ${HOME}, then redirects it to a file, called .ls (In UNIX, any file that begins with a '.' is hidden, unless you perform an ls -a). then we wait for an hour. Then we take another 'ls -l' and redirect it to a separate file, .ls-comp. Then, the diff command is executed, which searches for any differences in the files. If one is found, it is displayed. Otherwise, it exits with no return value. Let's play with this some though, I'm not happy with it:

# ls.sh part 2 original modified to send user warning of file change.
while echo "Perpetual loop" > /dev/null
do
ls -l ${HOME} > ${HOME}/.ls
sleep 1h
ls -l ${HOME} > ${HOME}/.ls-comp
if diff ${HOME}/.ls ${HOMR}/.ls-comp
then echo "this is ignored, since it is being redirected to /dev/null" > /dev/null
else
clear
ECHO "FILES CHANGED!"
break
fi
done

Lets start from the first line, this script invokes what's called conditional logic, which states basically that if something is true (no returned value in the instance of shell commands), then the script should do something. The first conditional logic we will use is the while statement. The while statement will continue to do what is between do and done until either it is broken out of, or the test returns a non-zero value (it returns text, whatever.). While follows:
while command_that_returns_no_value
do
some commands
done
Now then, the command that we want while to test is going to be echoing something to the null device, /dev/null, which returns no output 100% of the time. This creates a perpetual loop, until we break out of it. The rest is the same, up to the if statement, which follows:
if command_that_returns_no_value
then do something
else do something different
fi
the fi ends the if loop. What we are now saying is that if when we compare the diff output, if it returns no value (is true), then we want to do nothing, which in this case is another practice in using /dev/null. Now, if it returns other than zero, it means that a change has been made, which echos a nice little warning, then breaks out of the while loop, exiting the script. Well, I'm still not happy!!!
insert the following line after the warning, but before break:
diff ${HOME}/.ls ${HOME}/.ls-comp | mail yourusername@yourdomain
This will email you the contents of the output of the differences in the files. Now, if you really want to be cool, you'll strip the break command out, so it will email you each time that a change is made, continuously running. Also, you'll take out the echo "CHANGES...." part out, which will thus print no info, and invoke it like:
./ls.sh &
As you will remember, the & after a command tells the shell to execute the command in the background. Nice, now you have a free VT (or telnet session) to monitor any changes made to your directory.
As I'm sure you're getting the picture of, UNIX can be tailored to fit your individual needs. The only limitation is yourself. If you feel you need something that others can't provide, or if you want to do it yourself, you CAN! And its relatively easy, as opposed to writing C code all day long. Now, you basically should get the idea of how scripting works. If not, I'll include some other scripts later on. Now though, we'll go into aliasing and linking, and modifying your search path. First, the search path:
$PATH = ${PATH}:/directory_you_want_included
will add an entry to the PATH variable. Place this line in your .bash_profile, or /etc/profile (for all users). This will search your directory for a given command, after all others. You can simply:
$PATH = /dir_you_want:${PATH}
If you want it first. I prefer it to be last.
Now, what about aliasing? Aliasing is used to make one "command" act as if you entered another. Type alias at a prompt to see all aliases. To add an entry:
alias desired_command_name='real_command_name'
will create the alias of desired to real. For instance, a common alias is alias ls='ls --color' which causes the default 'ls' to output 'ls --color' each time.
Now, about linking. Linking is used to link one file to another. For instance, lets say you want a link in your home dir to /home/some_user/their_mp3s:
ln -s /home/some_user/their_mp3s ${HOME}/cool_mp3s
will create a "directory" in your home directory called cool_mp3s, which acts as if it were the target directory. Thus, you can 'cd cool_mp3s' to cd to that user's mp3 directory.

Well, that does it for this tutorial, check out the website above for more information such as this.