Bash Tips & tricks
Here are some tips & and tricks about bash that I consider useful and use regularly. This is not a bash tutorial, some bash knowledge is assumed.
Some words about the notation used:
- Environment variables and one line commands are shown this way.
- Paths and filenames are shown underlined, with fixed font and red foreground color: /usr/local/src
- A box like this:
var1 = 3indicates a piece of code or configuration file
somecomand - A box like this:
# command1indicates a sequence of commands.
# someothercommand - Keybindings are presented in the same way as the bash documentation. C-x e would mean to press at the same time control and x, and after release them press the key e. The meta key is represented by M.
Moving aroung directories
- Directory spellcheck: Bash can correct small typos when moving to another directory. Put the
line shopt -s cdspell on your .profile or .bashrc:
$ cd /eetc
/etc
$ cd /tm
/tmp
$
- Last directory and home directory: cd - takes you to the previous working directory.
cd
(without arguments) changes the working directory to the home of the user
- Directory stack:
- The command pushd . pushes the current directory path onto a directory stack. pushd somepath changes the directory to somepath and pushes the previous working directory on the stack.
- The command popd retrieves the path from the top of the directory stack and changes the current working directory to it.
- You can print the directory stack with dirs (the first directory on the left is the current directory and it is always on the stack). If you want to print one directory per line, use dirs -v
- "Memory" cd: You can get a cd with "memory" with an
alias to pushd. I use ,, as an alias to popd
~:$ alias cd="pushd &> /dev/null"Keep in mind that using an alias for cd means that you can no longer use cd without arguments for changing to your home directory.
~:$ alias ,,="popd &> /dev/null"
~:$ cd /etc/
/etc:$ cd /usr/src/linux-2.6.8.1/drivers/
/usr/src/linux-2.6.8.1/drivers:$ ,,
/etc:$ ,,
~:$
Environment variables
- CDPATH: This variable works like the PATH
variable, but with directories and the cd command. Consider the following example:
~:$ export CDPATH=.:/:/usr/src/
~:$ cd etc
/etc
/etc:$ cd linux-2.4.26
/usr/src/linux-2.4.26
/usr/src/linux-2.4.26:$
- Random numbers:: Each time you access the RANDOM env variable, a random number between 0 and
32767 will be generated. It is also easy to generate numbers between 0 and N, being N ≤ 32767:
$ # Generate some random numbers between 0 and 32767
$ echo $RANDOM
30501
$ echo $RANDOM
3113
$ echo $RANDOM
29804
$ # Generate some random numbers between 0 and 20
$ let N="$RANDOM % 21"; echo $N
2
$ let N="$RANDOM % 21"; echo $N
14
$ let N="$RANDOM % 21"; echo $N
15
$
- Timeout:: Sometimes it is useful to have a timeout on some shells (like a root shell).
This way after some period of inactivity the shell will exit. To do this in bash, set the variable
TMOUT to the number of seconds of inactivity that the shell will wait before exiting.
- A safe PATH: If you want to put the current directory
(.) on your PATH environment variable, put it always on the
last place. If you don't, some malicious user can screw you up!. Consider the folowing situation:
~:$ export PATH=.:/usr/bin/:/bin
~:$ cd /tmp
/tmp:$ cat ./ls
#!/bin/sh
rm -Rf ~
/tmp:$ # What will happen if type ls?
Useful keybindings (in Emacs mode)
Here is a list of the keybindings I use most in bash.
Keybinding | Action |
---|---|
C-a | Go to the beginning of the line |
C-e | Go to the end of the line |
C-l | Clear screen |
C-u | Cuts the text from the beginning of the line to the caracter before the cursor |
C-k | Cuts the text from the caracter under the cursor to the end of the line |
C-y | Paste text |
M-? | List all possible completions |
M-* | Insert all possible completions after the cursor |
M-. | Last command's last argument |
M-number letter | Repeats letter number times |
Macros
bash has support for recording and executing macros. You can start recording a macro with the keybinding C-x ( and stop it with C-x ). You can then execute the recorded macro with C-x e
Readline related
- History search: You can enable history search with the cursors. This way, you just have to type
the first letters of a command and push up to complete to a similar previous command. In my opinion, this is
much simpler than the default keybindings!. To enable this, add the following lines to your ~/.inputrc
or /etc/inputrc:
"\e[A": history-search-backwardThe previous behaviour of the up and down keys can be accessed using C-p and C-n
"\e[B": history-search-forward
- Completion of symbolic links to directories: One anoying feature (at least for me)
is that bash doesn't add the final '/' when it autocompletes the name of a symbolic link to a directory. I guess
this behaviour is to let know the user that the file in question isn't a directory, but a symbolic link to one.
Anyways, it is quite anoying having to press an extra tab. If you put the following
line in your ~/.inputrc bash
will add the missing slash when autocompleting symbolic links to directories:
set mark-symlinked-directories on
- No more double tab: The following line:
set show-all-if-ambiguous onwill make bash display multiple tab-completion options hitting the tab key only once.
Executing commands
- History search and execute: Typing !somecommand will execute
the most recent command line starting with somecommand
Shell scripting
- Reading line by line from a file: To read line-by-line the contents of a file from an script
you can use the following script construction:
while read LINE; do
# Do what you want here, $LINE contains the current read line
done < /path/to/somefile
- Reading a char-delimited formatted input: You can manipulate the environment variable
IFS (I think it means Internal Field Separators, but I'm not sure) to the
character that separates fields in the input:
$ IFS=":"
$ read login pass uid gid realname homedir shell < /etc/passwd
$ echo $login,$pass,$uid,$gid,$realname,$homedir,$shell
root,x,0,0,root,/root,/bin/bash
$ unset IFS # Or you can set it to the previous value, but you have to save it at the beginning!
- Arrays: Bash has builtin support for one-dimensional arrays. Here is how to
do some basic array operations:
- To create an array, just assign a value to an index : A[10]="hello"
- The index part of the assignment is evaluated as a mathematical expresion. This command assigns "hello" to position 3 of the array A: A[2*2-1]="hello"
- To access the value of some position of the array use the following syntax: ${A[10]}
- To access all the values of the array: ${A[@]}
- To get the number of elements of the array use: ${#A[@]}
- To delete a position of the array: unset A[10]
- To delete the entire array just use the common syntax for environment variables: unset A
Tab-completion on steroids
Try to install the package bash-completion. In debian it is installed by default. If it is installed on your system you should have the file /etc/bash_completion
The bash-completion package provides tab-completion behaviour for more things that only filenames: environment variables, ssh hosts, options for common commands.... To use it all you need to do is to source /etc/bash_completion.
The following example shows some (there is a lot more!) of the magic of bash-completion ([TAB] is used to represent a tab keypress):
$ ssh a[TAB]cm.asoc.fi.upm.es
$ ssh l[TAB][TAB]
laurel.datsi.fi.upm.es localhost
$ ssh l
$ tar [TAB]
A c d r t u x
$ tar
$ ls --c[TAB]
--classify --color --color=
$ ls --c
$ echo $P
$PATH $PIPESTATUS $PPID $PRFDIR $PS1 $PS2 $PS4 $PWD
$ echo $P
This tip was contributed by Ramón Pons
A better ~/.profile
I don't like having all my configuration (aliases, environment variables, etc...) on just one file. It looks quite messy to me, and making changes becomes increasingly difficult as the file grows. So instead of having one big ~/.profile I have several small, specialized files in a directory called ~/.profile.conf. The files are the following:
- ~/.profile.conf/aliases Aliases, using the syntax commandalias:command arg1 arg2...
- ~/.profile.conf/autoexec This file gets executed each login
- ~/.profile.conf/autosource This file gets sourced each login
- ~/.profile.conf/env Environment variables. The syntax is the usual: somevar=somevalue
- ~/.profile.conf/path List of directories to add to my $PATH variable, one directory per line
Of course, an special ~/.profile script is needed to read all these files. Here is my ~/.profile:
PRFDIR=~/.profile.conf # Aliases if [ -f "$PRFDIR/aliases" ] ; then IFS=":" while read ALIAS CMD; do if [ "$ALIAS" != "" ] && [ "$CMD" != "" ] ; then alias $ALIAS=$CMD fi done < $PRFDIR/aliases unset IFS fi # Environment variables [ -f $PRFDIR/env ] && . $PRFDIR/env # PATH variable if [ -f $PRFDIR/path ] ; then while read LINE; do PATH=$PATH:$LINE done < $PRFDIR/path fi # Execute and source start scripts if [ -f $PRFDIR/autosource ] ; then . $PRFDIR/autosource fi if [ -f $PRFDIR/autoexec ] && [ -x $PRFDIR/autoexec ] ; then $PRFDIR/autoexec fi