Penguin

What's true and what's false?

0 is true, anything non-zero is false. You can test the return value of any command by examining the special $? EnvironmentVariable.

$ ls mbox ; echo $?
mbox
0
$ ls mboxxx ; echo $?
ls: mboxxx: No such file or directory
1

There are also special commands called true(1) and false(1) that can be used in tests:

$ true ; echo $?
0
$ false ; echo $?
1

How do I stop bash beeping at me all the time?

It's probably not bash(1) beeping, but readline(3) sending a bell. You can disable this by putting:

set audible-bell none

in your ~/.inputrc file. This will remove the bell for all readline(3) enabled programs. To make it only do this for bash(1), see the readline(3) section of the bash(1) manpage.

If you are in a graphical environment, you can tell your XServer to disable all beeps for X programs (including xterm(1) and other terminals) by setting the bell volume to zero:

xset b 0

If you use gnome, then that annoying beeping can be disabled by going to System > Preferences > Sound

Find the "System Beep" tab and uncheck "Enable System Beep".

How do I do arithmetic in bash?

Surround the expression with $(( )), eg:

ANSWER=$((6*9))

This is equivalent to using the expr(1) program, but you will need to escape any shell special characters:

ANSWER=`expr 6 \* 9`

How do I wait until a specified time?

You could use at(1) but it's more fun to use something like this:

sleep $(($(date -d '6pm tomorrow' +%s)-$(date +%s)))

What's << doing in all my files?

See HereDocuments.

How can I easily do a batch rename of files with a subtle change (ie . to -)?

Easy:

for i in *.files.foo; do mv $i ${i/files.foo/files-foo}; done

This lets you use sed(1)-like regexp syntax in the command line. You might want to read up on "Parameter Expansion" in bash(1).

To remove a suffix:

for i in *.files.foo; do mv $i ${i%.foo}; done

To remove a prefix:

for i in files-foo*; do mv $i ${i#files-}; done

How do I get a sequence of numbers

For small ranges you can just write the numbers out:

for i in 2 3 4 5 6; do echo $i; done

On GNU systems, use the seq(1) program:

for i in `seq 50 1000`; do echo $i; done

FreeBSD has a similar program called jot.

How do I insert a Tab character when \t won't work?

Type Ctrl-V so the next character will be interpreted literally, then tap Tab to insert a literal Tab character.

How does redirection work?

You can redirect a program's stdout and/or stderr to places other than the terminal – eg to files on the disk, or as another program's stdin. The order that redirections happen can be important. Some examples.

Make stderr go to stdout, so you can view both of them together in less(1) or more(1):

somecommand 2>&1 | less

Get rid of stdout, and move stderr to stdout so that you can pipe it to less:

somecommand 2>&1 >/dev/null | less

The following first sends stdout to /dev/null, and then sends stderr to where stdout is now pointing (ie sends both to /dev/null, which is probably not what you want for redirection, but it's excellent for getting silent output from a command):

somecommand >/dev/null 2>&1

As an alternative, to send both stdout and stderr to the same place you can use the &> redirector:

somecommand &>/dev/null

See CshProgrammingConsideredHarmful for some trickier examples.

How does redirection work from inside a script?

A useful method to debug bash scripts is to run then with bash -x; printing each line as it goes, interspersed with its output. However, you can't start a script with

#!/bin/bash -x > foo

You can, however, do this:

#!/bin/bash -x
exec 1> /tmp/foo
exec 2>&1

How can I get the output from a command into a program that acts on files, not stdin?

Using process substitution (see bash(1)Part4) -

gedit <(groff-2-wiki.pl bash)

This opens up the bash(1) wikified page in gedit (as a temp file like /dev/fd/63), ready to manually overview and paste into the wiki. See the file archive for the groff-2-wiki script.

Bash isn't using the full width of the terminal after I resize it

When you resize a terminal, it sends a SIGWINCH signal to the process running inside it. If the current foreground process is not bash(1), but another process (eg less(1), tail(1), or whatever), then only that process receives the signal. The shell is then left with outdated size information.

The best way to fix this is to set the checkwinsize shell option:

shopt -s checkwinsize

Bash will now check the size every time it displays a prompt. Other shells (such as zsh(1)) seem to do this automatically, and don't have a corresponding option.

A one-off workaround is to send SIGWINCH to the shell yourself:

kill -WINCH $$

($$ is a variable containing the shell's own PID.)

How do I change the title of a terminal?

From faqs.org:

Window and icon titles may be changed in a running xterm(1) (and most derivatives, such as gnome-terminal) by using XTerm escape sequences. The following sequences are useful in this respect:

  • ESC]0;stringBEL – Set icon name and window title to string
  • ESC]1;stringBEL – Set icon name to string
  • ESC]2;stringBEL – Set window title to string

where ESC is the escape character (\033), and BEL is the bell character (\007).

echo -ne "\033]0;''title here''\007"

In bash(1), to have the title automatically update based on who and where you are, set PROMPT_COMMAND like so:

ROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'

How do I enable case-insensitive tab completion?

  • bind "set completion-ignore-case on"

That should configure the shell completion to be case-insensitive for the remainder of the session.

Make permanent: Create a file called .inputrc in your home directory and put this line in it:

  • set completion-ignore-case on

Help, bash is listing my files weird!

Well, bash is listing nothing. It's ls(1) and its locale support that matter here, it has nothing to do with the Shell. But this is where you'd expect to find this tidbit.

I swear ls(1) used to list files in ASCII order. Then one day it stopped. And I didn't like it. Much swearing and cursing ensued.

$ ls -la
total 3612
drwxr-xr-x  15  573  573    4096 2005-05-14 14:53 .
drwxrwsr-x   4 root src     4096 2005-06-15 01:59 ..
drwxr-xr-x  20  573  573    4096 2003-08-25 23:44 arch
-rw-r--r--   1 root root   17644 2005-05-14 14:44 .config
-rw-r--r--   1 root root    4376 2005-05-14 14:46 .depend
-rw-r--r--   1  573  573   18691 2002-08-03 12:39 COPYING
drwxr-xr-x  32  573  573    4096 2005-04-04 13:42 Documentation
drwxr-xr-x  40  573  573    4096 2005-05-14 14:45 drivers
-rw-rw-r--   1  573  573   19095 2005-05-14 14:44 Makefile
drwxr-xr-x   2  573  573    4096 2005-05-14 14:49 mm
-rw-r--r--   1  573  573   14287 2003-08-25 23:44 README
-rw-r--r--   1 root root  505139 2005-05-14 14:53 System.map
-rw-r--r--   1 root root       2 2005-05-14 14:46 .version
-rwxr-xr-x   1 root root 2646164 2005-05-14 14:53 vmlinux
$ export LANG=C LC_ALL=C
$ ls -la
total 3612
drwxr-xr-x  15  573  573    4096 May 14 14:53 .
drwxrwsr-x   4 root src     4096 Jun 15 01:59 ..
-rw-r--r--   1 root root   17644 May 14 14:44 .config
-rw-r--r--   1 root root    4376 May 14 14:46 .depend
-rw-r--r--   1 root root       2 May 14 14:46 .version
-rw-r--r--   1  573  573   18691 Aug  3  2002 COPYING
drwxr-xr-x  32  573  573    4096 Apr  4 13:42 Documentation
-rw-rw-r--   1  573  573   19095 May 14 14:44 Makefile
-rw-r--r--   1  573  573   14287 Aug 25  2003 README
-rw-r--r--   1 root root  505139 May 14 14:53 System.map
drwxr-xr-x  20  573  573    4096 Aug 25  2003 arch
drwxr-xr-x  40  573  573    4096 May 14 14:45 drivers
drwxr-xr-x   2  573  573    4096 May 14 14:49 mm
-rwxr-xr-x   1 root root 2646164 May 14 14:53 vmlinux

Dealing with argument lists in wrappers

If you have a shell script that passes arguments on to another program while acting as a wrapper watch carefully how you deal with arguments or you will end up accidentally eating whitespace. See the example below for a demonstration.

test.sh
#!/bin/bash
# Print number of arguments
echo $#
wrapper.sh
#!/bin/bash

# Do some stuff here

# Call another program
./test.sh $@
safe_wrapper.sh
#!/bin/bash

# Do some stuff here

# Call another program
./test.sh "$@"
Output
matt@argon:/tmp$ ./wrapper.sh
0
matt@argon:/tmp$ ./safe_wrapper.sh
0
matt@argon:/tmp$ ./wrapper.sh a
1
matt@argon:/tmp$ ./safe_wrapper.sh b
1
matt@argon:/tmp$ ./wrapper.sh a ""
1
matt@argon:/tmp$ ./safe_wrapper.sh b ""
2

Note that the 'empty' argument is only preserved when $@ is wrapped in quotes (eg in safe_wrapper.sh).

Why can't I hit Del? Why does it show a tilde (~) character ?

readline(3) isn't correctly interpreting the escape code that delete generates. You can work around it in your terminal, but the easiest fix is to add the line

"\e[3~": delete-char

to ~/.inputrc.

Garret LeSage has posted a super useful inputrc, which fixes all sorts of keys (such as ctrl-arrow).

Here's another cool self-documented tip:

# By default up/down are bound to previous-history
# and next-history respectively. The following does the
# same but gives the extra functionality where if you
# type any text (or more accurately, if there is any text
# between the start of the line and the cursor),
# the subset of the history starting with that text
# is searched (like 4dos for e.g.).
# Note to get rid of a line just hit Ctrl-C.
"\e[B": history-search-forward
"\e[A": history-search-backward

How do I feed quote characters to the other end of an SSH session?

I have a bunch of remote machines with a config file that has some OPTIONS=something, and I want to set them all across-the-board to OPTIONS="foo". Locally, I would do it this way:

sed -i -e 's/OPTIONS=.*/OPTIONS="foo"/' file

This is hard to send over SSH however, as bash(1) locally likes to eat all your quotes, so they never reach the other end.

PerryLorier's most bodacious answer:

ssh site sed -i -e \''s/OPTIONS=.*/OPTIONS="foo"/'\' file

If anyone else has an answer, please add it, rather than replacing this one; I can at least understand what happens here. One set of brackets is eaten locally, and another set is sent to the other end, so the sed command ends up with 's when run on the remote host. —CraigBox

Another option:

ssh site <<'END_SCRIPT'
sed -i -e 's/OPTIONS=.*/OPTIONS="foo"/' file
END_SCRIPT

AristotlePagaltzis

LawrenceDoliveiro's so-simple-you'll-kick-yourself-for-not-thinking-about-it answer:

ssh site sed -i -e $(printf %q 's/OPTIONS=.*/OPTIONS="foo"/') file

using Bash's printf builtin.

How can I see what my command will look like after bash expands it?

After typing the command, press Esc then press C-e

Bash C-style for loop construct

for ((i=1; i<100;i++));do echo $i; done;

How can I do Division in bash using floating point numbers?

Use bc

  • echo "scale=10;5/4" | bc"

will result in : 1.2500000000 The value of the scale function is the number of digits after the decimal point in the expression. (In this case 10)

How Can I use and / or in bash if statements?

  • You can do boolean OR in BASH by using the -o operator.
  • You can do boolean AND in BASH by using the -a operator.

Try intermixing the two also.

if [ $bla -eq 1 -o $cars -eq 3 -o $monkeys -eq 4 ]; then
    echo "Westside"
fi

How can I supply filenames with a prefix of '-' '--' as arguments to a program?

Say you have a file called '--test'. If you wanted to use vim to edit this file then you would do:

vim -- --test

The '--' tells the command that what follows is an argument, NOT to interpret them as options.

AdrianHo: Note that not all programs accept '--' as an "end of options" marker. A more reliable method is prefixing the filename with "./", as in:

vim ./--test

How to directly interpret a shell script by the current shell. (Without forking a subshell)

  • . <myscript>

If the above script makes a change to the environment, it is the environment of this shell that is changed. So if inside the script we change the directory, then when the script has finished being interpreted we will find ourselves in the new directory.

Note : This can be handy when you want to reset the shell environment variables.

Just do:

  • . ~/.bashrc

Set vi mode in bash

Vi mode allows for the use of vi like commands when at the bash prompt. When set to this mode initially you will be in insert mode (be able to type at the prompt unlike when you enter vi). Hitting the escape key takes you into command mode. To enable this do:

  • set -o vi

One thing I do like about vi mode, is how easy it is to edit really long commands. (Of course some will argue that I should just use a bash script instead)

When in vi mode press 'esc' to enter command mode. Now press 'v'. This will start up the default editor (as defined by the EDITOR environment variable) and your command will be displayed. Edit it to your liking (feel free to use multiple lines for loops/if statements etc), save and exit. Your command will be executed.

To escape out of vi mode simply type:

  • set +o vi

See also


CategoryNotes