the ins and outs of bash
| April 3rd, 2008You may already be familiar with many of the programs that can run in a *nix terminal. These programs are run by entering commands switches/options/parameters in a shell and ZOOM away they go. One of the popular shells is gnu bash. With this post I’d like to outline conceptually how I look at, and approach bash.
The list below enumerates some of the features in bash, they are general descriptions. Of course bash can do many other things…
Bash can:
- execute *nix commands
- print strings of characters to the screen
- store strings of characters in variables
- read strings from variables
- write the output of a command to a file
- read characters from a file
- do logic tests and basic integer math
- return integers to stderr
- use the output of a command as input for another command
In the next few paragraphs I’ll demonstrate how to do the things in the above list. Once you have a grip on those basic features you will be ready to do more fancy things with bash.
Type in some characters and run a command
Almost every program run in bash takes arguments or options parameters. Let’s use for an example the date command.
Entering the command:
date +%FOn my computer the output is:
2008-04-03which is todays date. the last part of the command +%F tells the date program how we want to format the out put when we execute date. If you run date with no format string you should get a different result
On my system if I run:
date
I get this out:
Thu Apr 3 11:51:29 CDT 2008you can learn all about the different format options available by executing man date.
Create a variable and save a string to it
Lets pretend I really like a format string, maybe I like it so much that I want to save it to a variable. In this case +%F will get saved. First we’ll create the variable. The simplest way is to just enter the variable name on the command line followed by an equals sign (=). And we can assign it a value by entering some characters to the right of the equals sign. NOTE: I think it’s tradition to use all uppercase names for variables in bash.
MYDATEFORMAT=+%Four variable MYDATEFORMAT now holds the string +%F. To do a better job we could have put quotes around the string, but you don't HAVE to.
Use the value saved in the variable
we can now substitute like this:
date $MYDATEFORMAT
and get the same output as before when we entered date +%F:
2008-04-03
So what we showed there was bash reading something out of a variable. And we see that bash can substitute the the contents of a variable or expand the variable before it executes a command.
Ok, lets have a look at the which command. The which command is used to show you which file will be used when you type in a command. For example on my system:
which dateresults the printing of the path to my date command:
/bin/date
Save the output of a command to a variable
Now we can get fancy and save the output of the which to a variable called PATHTODATE
PATHTODATE=$(which date)now /bin/date
Use echo to print the values of variables to the screen
We can use echo to print the string saved in a variable to the screen:
echo "$PATHTODATE"The \n after the available name causes echo to print a newline character to the screen after the variable name.
Which, on my system will show:
/bin/dateKeep in mind now this is a string we saved. If we move the file located at /bin/date our variable will still hold /bin/date.
So far we have:
- declared a variable and written a string to it
- used the string stored in a variable
- assigned the output of a command to variable
Write the output of a command to a file
The technical term for what we are doing here is "redirection", which means we are redirecting what would normally get printed to the screen. We are going to redirect the output of our command to a file.
The command we will use is ls, we will run it the first time with out redirecting it. The ls command is a very useful command. It will print to the screen a list of files and directories. If you are in your home directory, then the out put will be a list of files in your home directory. When I run:
ls
On my computer the output looks like this:
32.jpg gnubie receipt.pdf
asap.txt GNUBIE.html res.doc
bashref.html GNUBIE.html_files snapshot1.png
bin mysqlPhpTest.php snapshot2.png
builtinsListTable.html nothing snapshotsForLee
comics penrose test.txt
dateFormat.txt postcard_back.pdf wallpaper8.jpg
Desktop postcard_front.pdf wallpaper9.jpg
Examples questionsForErin.txt wpUpgrade
if your out put looks the same as mine, e-mail me right away because we are probably twins seperated at birth.
Ok, lets write the output of that command to a file now.The file will be created if it does not already exist. Here is your command... this will write the output of ls to a file called output_of_ls.txt:
ls > output_of_ls.txtso the > is the redirection operator.
As an aside you can run ls again and see that the file listing now includes output_of_ls.txt.
If I open the text file output_of_ls.txt in a text editor on my system, I see this:
32.jpg
asap.txt
bashref.html
bin
builtinsListTable.html
comics
dateFormat.txt
Desktop
Examples
gnubie
GNUBIE.html
GNUBIE.html_files
mysqlPhpTest.php
nothing
output_of_ls.txt
penrose
postcard_back.pdf
postcard_front.pdf
questionsForErin.txt
receipt.pdf
res.doc
snapshot1.png
snapshot2.png
snapshotsForLee
test.txt
wallpaper8.jpg
wallpaper9.jpg
wpUpgrade
The list items are no longer in columns. That's a bit odd... well after digging around the internet I found an explanation at this web page: http://www.opengroup.org/onlinepubs/000095399/utilities/ls.html
The relevant part reads like this:
...an implementation-defined number of column positions shall be assumed, based on the implementation's knowledge of the output device. The column width chosen to write the names of files in any given directory shall be constant. Filenames shall not be truncated to fit into the multiple text-column output. ...
Basically it is saying, if it is printing to the screen the ls command tries to figure out a fancy way to print the output.
Reading from a file
I hope this example isn't overly simple. What I want to do here is just show that using only bash you can read out the contents of a file. In this case we are going to print them to the screen. In other posts I'll try and sow more practical uses of reading from files. For now I want to ues the simplest example possible for demonstration.
Here is a super simple example of how to read from a file using only bash:
echo "$( < output_of_ls.txt )"
And the output looks like this:
32.jpg
asap.txt
bashref.html
bin
builtinsListTable.html
comics
dateFormat.txt
Desktop
Examples
gnubie
GNUBIE.html
GNUBIE.html_files
mysqlPhpTest.php
nothing
output_of_ls.txt
penrose
postcard_back.pdf
postcard_front.pdf
questionsForErin.txt
receipt.pdf
res.doc
snapshot1.png
snapshot2.png
snapshotsForLee
test.txt
wallpaper8.jpg
wallpaper9.jpg
wpUpgradeIf I don't use quotes:
echo $( < output_of_ls.txt )then we get this blob of text with no newlines:
32.jpg asap.txt bashref.html bin builtinsListTable.html comics dateFormat.txt Desktop Examples gnubie GNUBIE.html GNUBIE.html_files mysqlPhpTest.php nothing output_of_ls.txt penrose postcard_back.pdf postcard_front.pdf questionsForErin.txt receipt.pdf res.doc snapshot1.png snapshot2.png snapshotsForLee test.txt wallpaper8.jpg wallpaper9.jpg wpUpgrade
Of course it is possible to write scripts for/in bash, which are read line by line and executed. But the method shown above demonstrates that text can be read from a file and used in the same way as a string stored in a variable.
Syntax Aside - Recap
Maybe it would be a good place to recap some of the syntax. lets use the echo bash builtin command and have a little fun.
We can start by writing some text to a variable and some other text to a file:
QUESTION='I wonder what time you will read this?'
echo Hope this made sense to you - bye! > bye.txt That will give us something to play with.
Here I'm showing how we can use string literals, the output of a command, or a line read from a file...
echo Hi, I like bash. "$QUESTION" I wrote this: "$( date )". "$(< bye.txt)"
And the output on my system is:
Hi, I like bash. I wonder what time you will read this? I wrote this: Mon Apr 7 02:16:10 CDT 2008. Hope this made sense to you - bye!I hoped that showed well enough how to use those outputs values etc.
Sending the output of one command to the input of another command
For this example I'm going to use two commands that are not builtin to bash. The part that is built into bash is the pipe.
When I run the env command I get a list of a bunch of variables that are set. It looks like this:
SSH_AGENT_PID=5321
TERM=xterm
SHELL=/bin/bash
WINDOWID=31457316
XTERM_SHELL=/bin/bash
USER=jonny
LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35:*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35:
GNOME_KEYRING_SOCKET=/tmp/keyring-5A628d/socket
SSH_AUTH_SOCK=/tmp/ssh-RrJTfE5288/agent.5288
SESSION_MANAGER=local/taft:/tmp/.ICE-unix/5332
USERNAME=jonny
PATH=/home/jonny/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
DESKTOP_SESSION=xfce4
GDM_XSERVER_LOCATION=local
PWD=/home/jonny
LANG=en_US.UTF-8
GDMSESSION=xfce4
HISTCONTROL=ignoreboth
XTERM_LOCALE=en_US.UTF-8
XTERM_VERSION=XTerm(229)
SHLVL=1
HOME=/home/jonny
LOGNAME=jonny
XDG_DATA_DIRS=etc/xdg/xubuntu:/usr/local/share/:/usr/share/:/usr/share/gdm/:/usr/share
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-ErhN7Co8a2,guid=6a9ea55e09d50783d5789b0047f98476
LESSOPEN=| /usr/bin/lesspipe %s
WINDOWPATH=7
DISPLAY=:0.0
LESSCLOSE=/usr/bin/lesspipe %s %s
XAUTHORITY=/home/jonny/.Xauthority
_=/usr/bin/env
humf...I wish they were in alphabetical order!
Well, now since I would like the output of the env command sorted, I'll go ahead and pipe it to the sort command, like so:
env | sort
Doing that, I get this:
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-ErhN7Co8a2,guid=6a9ea55e09d50783d5789b0047f98476
DESKTOP_SESSION=xfce4
DISPLAY=:0.0
GDMSESSION=xfce4
GDM_XSERVER_LOCATION=local
GNOME_KEYRING_SOCKET=/tmp/keyring-5A628d/socket
HISTCONTROL=ignoreboth
HOME=/home/jonny
LANG=en_US.UTF-8
LESSCLOSE=/usr/bin/lesspipe %s %s
LESSOPEN=| /usr/bin/lesspipe %s
LOGNAME=jonny
LS_COLORS=no=00:fi=00:di=01;34:ln=01;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.gz=01;31:*.bz2=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.avi=01;35:*.fli=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.flac=01;35:*.mp3=01;35:*.mpc=01;35:*.ogg=01;35:*.wav=01;35:
PATH=/home/jonny/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
PWD=/home/jonny
SESSION_MANAGER=local/taft:/tmp/.ICE-unix/5332
SHELL=/bin/bash
SHLVL=1
SSH_AGENT_PID=5321
SSH_AUTH_SOCK=/tmp/ssh-RrJTfE5288/agent.5288
TERM=xterm
USER=jonny
USERNAME=jonny
_=/usr/bin/env
WINDOWID=31457316
WINDOWPATH=7
XAUTHORITY=/home/jonny/.Xauthority
XDG_DATA_DIRS=etc/xdg/xubuntu:/usr/local/share/:/usr/share/:/usr/share/gdm/:/usr/share
XTERM_LOCALE=en_US.UTF-8
XTERM_SHELL=/bin/bash
XTERM_VERSION=XTerm(229)
the | sent the output of env to the input of sort and gave us a nice sorted list.
That's about all I know
Not really, but I think if you understand what was going on in this post you should have a pretty easy time reading the rest of the code on my blog. I hope this post gave you a good understanding of how you can read and write to files and variables, print things to the screen, redirect and pipe the output of commands.
I didn't talk about using read and $REPLY. Because those seem to make more sense within the context of scripting. But, I'll mention them because they are used to read lines from the keyboard and save them into a variable... the $REPLY variable to be precise.
When you are not scripting and just typing in commands read still has it's uses but I wouldn't consider them as basic.