bash (bourne-again shell) is a Unix shell by GNU. It's the default shell of all common Linux distributions so chances that you get a bash on your server are quite big. To check if it's really bash you can use the command
$ echo $SHELL |
Unix shells allow you to execute commands. For the people coming from a Microsoft background it's comparable to (though usually more powerful than) the DOS prompt.
Bash also provides more sophisticated functions to work effectively. For example there is a command history to easily access commands entered in the past. bash provides you with a job control to run different applications in the same shell. Additionally it has builtin flow control and other programming possibilities[1].
When you enter the name of a command to be executed, the shell variable (See section Shell Variables) PATH is searched until a path is found where a executable with that name is located. The single paths are separated by a colon. The content of this variable on my machine currently looks similar to this:
$ echo $PATH /home/patrice/bin:/usr/local/bin:/usr/bin:/bin |
Suppose you enter the command
$ touch test_file |
touch is the command name and test_file is the argument. So the shell first searches a program named touch in the path. To do that it looks in /home/patrice/bin for an executable with that name. If there is one, it is executed. If there is none, the shell searches in /usr/local/bin. It continues like this for every entry in the path. If and only if no component of the path contains a file with that name, you get the error “command not found”. If one directory contains a file with that name, but it is not executable you get the error message “Permission denied”.
If you enter any directory information before the executable name, the shell doesn't try to locate the binary but assumes that you know better:
$ /bin/touch test_file $ ../runit $ ./testing |
All of these have a path information in it. The first one always takes the touch binary from the directory /bin, no matter what the search path looks like. The next command looks in the parent directory for a executable named runit. The last one is especially interesting. It executes the program testing in the current directory. This syntax is used very ofted, because it frequently happens that you want to execute a binary in the current directory.
Completion is another powerful feature of the bash. It works in the following contexts:
command
path
variable
username
hostname
The command and path completions are the two facilities that are used most often.
To get all possible completions, just press the tab key twice:
$ less /etc/ho<tab><tab> host.conf hostname hosts hosts.allow hosts.deny hotplug/ $ |
As /etc/ho is ambiguous, it gets completed and all possible expansions are shows. If you enter /etc/hot and then press the tab key once, it get's completed to /etc/hotplug/. See bash(1) and search for “Completing” to get more information.
Shell variables can contain any value. The shell provides some variables to query the current status, while other shell variables can be set by the user to configure some settings or to be fetched back later. The variable names are case sensitive.
To insert the value of a shell variable at a place in the command line, just prepend the shell variable with a dollar sign[2]. For example this printss out your user name:
$ echo $USER |
Sometimes you want to include variables in the middle of a word. Suppose this example:
$ echo $USERistesting |
This doesn't print the expected “patriceistesting”. Instead you get no output at all. To fix that, you have to write curly braces around the variable:
$ echo ${USER}istesting |
To set the value of a shell variable, assign the value with an equal sign[3].
$ weather="sunny" $ echo "The weather today is $weather" |
This sets the variable for the current process, but any processes which is called from this one, doesn't get the value of this variable. If you want to make sure that the value gets passed to other processes you need to export it.
$ weather="sunny" $ export weather $ ./weather_program |
Or with a bit less typing:
$ export weather="sunny" $ ./weather_program |
A lot of your work on a Unix system has to do with moving around on the file system. You can get your current working directory with the command pwd. After opening the session, this is usually /home/$USER. To change the current directory use cd. For example
$ cd /tmp |
As minimalist as this command may be, it has two nice tricks. First, you can move to your home directory by entering cd without any parameters. Another way of accessing the home directory is the tilde (˜). This works in any path information.
$ cd ~/data |
This example changes to the directory data in your home directory.
Additionally you can use the dash as parameter to change to the previous directory:
$ cd - |
pushd and popd are two great commands for anyone who works with a lot of directories. They store the previous directories on a stack. When you use pushd with a path as parameter, the current directory gets added to the stack and then the working directory is changed. Issue popd (without any parameter) to change to the directory on top of the stack.
$ pushd /tmp /tmp ~ $ pushd /var/log /var/log /tmp ~ $ popd /tmp ~ |
The command ls lists the files and directories in the current directory if executed without any parameters. The parameter used most often is `-l', which shows a more detailed lists. The `-a' parameter is useful if you want to list all files while the default behavior is to not list hidden files[4].
If you want to get the details of only one file, use the command:
$ ls -l $FILENAME |
This doesn't work for directories, because ls assumes you want to list the files in that directory. You can use the parameter `-d' to circumvent this problem. With that parameter, ls actually displays the details of the directory instead of the files in that folder.
$ ls -l -d $FOLDER |
ls(1) lists all available command options.
Every process has two output streams and one input stream:
Standard Output (stdout)
Standard Error (stderr)
Standard Input (stdin)
With redirection it's possible to place the output in a file or read the input from a file. For example you can write the error output of a program to a log file.
This is done with the `>' operator. For example:
$ locate '*.txt' >files.log |
The example redirects the standard output of the locate command to the file files.log in the current directory. So all found files are put in that file.
To redirect standard error, prepend the bracket with the number two. The number before the bracket tells the shell what stream to redirect. Standard output has the number one while Standard error is numbered two.
$ locate '*.txt' 1>files.log |
That command has the same effect as the previous example, while the following example redirects all errors to the file error.log in the current directory[5] but display the search result on the screen:
$ locate '*.txt' 2>error.log |
You can also combine these two redirections:
$ locate '*.txt' >files.log 2>error.log |
To log both outputs in the same file, prepend the bracket with a ampersand. For example:
$ locate '*.txt' &>locate.log |
There is the trick to redirect output to /dev/null. This device accepts any input and just discards it. So it's the black hole of your Unix system. Suppose you execute a command with a lot of output but you only want to see if there are any errors. Just redirect the standard output to /dev/null and you don't see all the garbage.
To use a file as the standard input of a process, use the `<' operator - that's the opposite of the output redirection. Input redirection from a file isn't used as often as output redirection, because most interesting text processing application accept a file name as parameter and only use standard input if no file is given.
One example for a tool that only reads data from standard input is tr. This is a great utility to do some simple text processing.
$ tr 'a-z' 'A-Z' <input.txt |
This transforms all lower case characters of input.txt to upper case and prints the result to standard output. You can now redirect this output to another file or just look at it with astonishment.
Don't redirect standard output to the file standard input is coming from. For example this example doesn't work:
$ tr 'a-z' 'A-Z' <input.txt >input.txt |
This has different reasons. First of all, the output redirection operator truncates the output file when the command is executed. If that was not the case it would still not work: redirection is synchronous. So it may be that not all data has been read from the file when the output stream provides its first output.
Piping is a very important and powerful technique of Unix shells. Let's start with an example:
$ locate '*.txt' | less |
This searches for any file with the extension “.txt” and then shows the result with less so that you can easily scroll through the output. By piping two processes together you essentially create a one-way communication channel between them. It redirects the output of the first process to the input of the second one. This is where redirection really gets powerful. For example you could also pipe the locate command above to the mail utility. An example says more than a thousand words:
$ find -atime +100 | mail systemservice@acme.com |
This mails a list of all files that haven't been accessed for more than 100 days. This is a perfect example for a scheduled task.
You surely see the analogy to redirection, don't you? The previous example can also be rewritten this way:
$ find -atime +100 >tmp $ mail systemservice@acme.com <tmp $ rm tmp |
But first it's more cumbersome, second it's less efficient and third you need to get a unique file name - especially if you execute this commands from a script.
Every command line you enter is stored in the history. There are two common ways to access the history.
The history is searchable. Press Ctrl+R and enter parts of a previously entered command. After entering the search term, repeat Ctrl+R until you get to the command line you want to execute. Just press enter and the command gets executed. If you want to edit the command before executing, press Ctrl+A or Ctrl+E. This stops the search and moves the cursor to the beginning or end of the line respectively.
The second possibility is the exclamation point. It is described in the bash man page in the section “Event Designators”. The most important technique is the exclamation point followed by a string. This executes the previous command starting with that string.
$ !less |
This example searches the history backwards for a command starting with “less” and executes it. So it basically opens the last file you have looked at with less. You can just enter two exclamation points to repeat the previously entered command[6].
If you aren't sure what command would be execute, append the string “:p” to the bang command. For example
$ !less:p |
prints the previous command that started with the string “less”, but doesn't execute it yet. If it is what you want, use the bang-bang command to execute it, as bash is intelligent enough to move the command at the first position of the history.
The bash allows you to execute several commands inside the same shell window. This has been very important in the past when no windowing was present. Nowadays it's a bit old fashioned but I still use it extensively. When accessing a server via ssh it's especially useful because opening a new session is usually a bit cumbersome.
Job control means that you can put a command in the background and later get it back. Suppose that you're in the bash man page and you want to try a command you have just read about. First press Ctrl+Z while reading the man page. This puts the man page in the background and gives you the shell back.
[1]+ Stopped man bash $ |
Now you can enter your commands. When you're ready to continue reading the man page, just enter
$ fg |
You can also put several jobs in the background. fg brings you to the last process that was active. Use the command jobs to print a list of the current jobs.
$ jobs [1] Stopped man bash [2]+ Stopped vi test [3]- Stopped man perl |
The plus sign indicates the job you are returned to if you just enter fg. To specify the job you want to put in the foreground, add the job ID.
$ fg 1 |
So now you can continue to read the bash man page.
An alternative syntax uses the percent sign instead of `fg':
$ %1 |
[1] | Programming is not covered in this document, there are good existing HOWTOs for that. | |
[2] | Note, that this notation is also often used in Unix literature to mark a place where you can insert something. For example:
This means, that you can replace te text “$FILENAME” with any filename you like. | |
[3] | Attention: Before and after the equal sign you must not use any spaces. | |
[4] | All files whose name starts with a dot are hidden. | |
[5] | Note that the file error.log also gets created/overwritten if stderr creates no output at all. This is always true for output redirection. | |
[6] | This is sometimes called bang-bang. |