This is a repost of a review that is timely, given this week’s focus on setting up your Linux server and changing all your computers over to Linux and so on.
I started this discussion a while back, and now it is time to continue it.
The Bourne Again Shell, bash, is the default command-line shell in Ubuntu and many *nix (Unix, Linux, etc.) systems. You can think of the shell as the most direct way to get into your operating system, and you can think of shell scripts (‘programs’ in essence) as macros that can automate computerized tasks.
For the present purposes, I’m discussing the book Learning the bash Shell (In a Nutshell (O’Reilly)) although the examples I’m providing are not necessarily from this text.
How to make a script
You make a script by making a text file, putting it in the right place on your computer with a correct name, throwing in a shebang line, writing correct bash code, changing permission on the script to be executable. This sounds like a lot of steps to go through, but once you’ve done it a few dozen times it becomes second nature.
Making the text file
Use a regular no-frills text editor. If you are using Ubuntu with Gnome, “gedit” is probably the way to go. If you are a masochist, use vi, ed, or emacs. Don’t use a word processor.
Put the file in the right place and give it a good name
The scripts you write should be in your home directory, so that when you upgrade your system, leaving your home directory intact (which is how you should always do upgrades) your work will still be there. Most likely there is a directory in home called “bin” and it is already on your shell’s path.
The path is the list of directories your shell searches for commands when one is executed. If there are multiple commands on your system with the same name, there is a certain order in which the shell searches for the command. This is a little complicated, which is why I’m connecting this post with a book chock full of details. Read the book.
You should avoid naming the script with a name that is already used for an existing command or program on your system. An obvious way to find out if a name is already in use is to run it. If you type a name in and it is in use, you can tell because either something happens, or nothing happens. If you type in a name and it is not in use, you get an error.
greg@greg-desktop:~/bin$ test
greg@greg-desktop:~/bin$ blfnig
bash: blfnig: command not found
The first try, with ‘test’ resulted in nothing. Nothing happening is one way the shell tells you that the command exists, and in this case, ran just fine. In contrast, ‘blfnig’ resulted in an error. So ‘blfnig’ could be a good name for a shell script you may right. Except it isn’t because it does not convey any meaning.
I’m going to write a script to test out script writing, but I can’t call it ‘test’ because there appears to already be such a command. so I’ll call it ‘testscript’
So I’ve got a text editor open, and now I need to put in the shebang line.
In bash, the symbol # is used to start a comment. But if you put this as the first line in your script, followed by the symbol ! and follow that with the path to where the bash shell itself (the program) lives, then you’ve got a shebang line. This line tells the system that this script uses this shell (bash) to run it.
I’m also going to make my program simple. It will simply say hello. To the world. As usual.
#!/bin/bash
A test script
echo hello world
Now, I save that file in the bin directory that is located in my home directory (there are other “bin” directories, as you just learned above). I can check to see if it is there by moving to that directory and running a file listing command.
greg@greg-desktop:~$ cd bin
greg@greg-desktop:~/bin$ ls
blogroll_java.txt blogroll_m2java.sh~ googcalc testscript~ test.sh~
blogroll_m2java fixblogroll now testscript.sh
blogroll_m2java~ fixblogroll.sh now~ testscript.sh~
blogroll_m2java.sh fixblogroll.sh~ testscript test.sh
greg@greg-desktop:~/bin$
Notice that it is there. Notice also that I don’t have a lot of other scripts here (this is not my main playing-around computer). Had there been a lot of stuff in here, I may have given the ls command some help to help me find what I’m looking for
greg@greg-desktop:~/bin$ ls test*
testscript testscript~
greg@greg-desktop:~/bin$
The extra ‘testscript’ with the squiggly thing must be something my text editor put there.
Make the script executable
Each file on a linux system has a number of attributes indicating ownership and permissions. By default, a file created with a text editor will not have “executable” permission. This does not mean you can’t execute it, but to do so you need to do special mojo. It is easier to simply change the permission.
Assuming you are you using your account on your own computer, all you need to do is to run the change mode command, like this:
chmod +x testscript
What is this arcanery, with the ‘plus-x’ thing? Read the book. It will work. Now, I can run my script using the name of the script and nothing else.
greg@greg-desktop:~$ testscript
hello world
greg@greg-desktop:~$
Notice that when you were not looking I changed back to my home directory. bash finds the script because the ~/bin directory is in my path.
(Not also that above the squggly thingie, the tilded ‘~’ on a filename meant, essentially, “this here file is the least important thing on your computer. Just some temporary backup made by a text editor, you can delete this” but here the ‘~’ is a shortcut code for “the user’s home directory, which is the most important thin on this computer, don’t erase it!” But I digress.)
I am nothing close to an expert on this [get yer troll bait here!] and I have to say that what I do know about this topic I did not really learn from “Learning the Bash Shell.” I learned some basic Unix stuff from using Unix computers years ago, and I’ve wallowed through over the last few years using various cheat sheets on the web and following examples provided by others. A few months ago I decided to learn more about bash, and read “Classic Shell Scripting” … a book I’ll discuss in a later post. But more recently I realized that I had fallen into the trap of having very uneven knowledge and getting stuck in places I really should not be stuck. So I picked up “Learning the bash Shell” to help fill in those gaps. The book assumes a fair amount of familiarity with computers and a strong resolve, and by the time you get to the end you may find yourself skimming chapters that are more about system administration tasks that a single-user machine does not necessarily require.
But it is a good way to get beyond mere fiddling around, and I recommend it.
Now, I’m about to publish this post, but since I started writing it some time ago, the “entry date” is way in the past. So I need a correctly formatted date giving me the current time in New York, where the Scienceblogs.com mother ship lives.
So could open a shell and enter:
date -d '+1 hour' '+%Y-%m-%d %H:%M:%S'
to get a properly formatted Entry Date for the Movable Type interface…
… or, since I do this all the time, maybe I should write a script that does this automatically….
UPDATE: With the change in Movable Type platform at Scienceblogs, the above script is rendered useless. Oh well.
Just want to make a very minor semantic correction (and only because, this one, I *know* I’m not going to mess up, not like that Asus thing): referring to /bin might be confusing to a newbie, though you correctly identified it in describing it at the beginning as a folder in your home. ~/bin and /bin are completely different — any path starting with / indicates it is a folder in the root of the file system. Any path starting with ~ means, start from your currently logged in user’s home folder. It’s nitpicky, but might as well display the path to the user as it shows up on the screen.
Also, this happened on my computer just now:
jthibeault@laptop:~$ touch ./bin/tester.sh
jthibeault@laptop:~$ chmod +x ./bin/tester.sh
jthibeault@laptop:~$ which tester.sh
jthibeault@laptop:~$ tester.sh
bash: tester.sh: command not found
Hmm, what have we here? It’s not working for me! More investigation:
jthibeault@laptop:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/lib/jvm/java-6-sun-1.6.0.12/jre/bin/
Well that’s interesting. My home folder isn’t on the path. A bit more digging and I discover normally that gets added to a default .bash_profile when you first set up a new user under Ubuntu.
jthibeault@laptop:~$ ls .bash_profile
ls: cannot access .bash_profile: No such file or directory
Strangely, it also doesn’t exist in /etc/skel — which is the skeleton user info that’s copied to all new users’ home folders. So something might be wrong with my install. Regardless, this can be rectified by adding this line:
export PATH=$PATH:/home/jthibeault/bin
to the end of my ~/.bashrc file (think of this as your user profile’s Start Menu Startup folder, or more accurately, autoexec.bat file in old DOS parlance). After a logout / login, I’d then have ~/bin on my path, and could run my (blank) tester.sh just fine.
Jason,
To continue the communal nit removing, am I right that you shouldn’t need to login/logout to get bash to rebuild the hash table? Whereas in csh, one can use rehash, bash will do a rehash automatically on failure to find the command.
My shell experience is old and like Greg’s was, incomplete.
I believe in bash it’s hash, not rehash. I’m not sure if it rehashes as you say, but it might. With bash there are multiple versions, and this is the kind of detail that might vary from version to version. We need a more advanced reference book … coming soon … to evaluate this.
I’ve updated the above post.
Greg, I first started reading your blog because of my interest in the evolution debate (I’ve been a member of t.o. since 2002) but I’m now finding an increased interest in Linux due to the information and resources you provide and link to. Although I was a programmer (emphasis on ‘was’), with a B.Sc. in CS, and currently own a computer sales and service company dealing exclusively with PCs, my change over on my personal machines to Ubuntu was in large part due to your blog.
Thanks.
w00t!!!! We got one!!!!
Another one. Can’t forget JanieBelle.
Forget JanieBelle? That’s why I drink!