Ok, this is going to be an article for beginners but with a spin at the end even for the most advanced users. Let's say you want to run a command inside of chroot as another user all in a single command and be back at your shell. How do you do it? Turns out it can be easily done by combining several commands in a beautiful way:

sudo chroot /chroot su - user -c "cmd args"

It may seem a pretty crazy combination of commands but it's all really easy to undestand. First, let's take a look at man chroot. It says,

sudo chroot /chroot command

Okay, so we can run command inside of chroot as root, but how do we run the command inside of chroot as another user? How about using su for command? Let's try that:

sudo chroot /chroot su - user

This runs su inside of chroot as user but we get an interactive shell. Let's take a closer look at man su. It says that we could try:

sudo su - user -c "cmd args"

This runs the cmd args through the shell. Great! So let's use this together with chroot again:

sudo chroot /chroot su - user -c "cmd args"

Excellent! Almost what we needed. However there is still one small problem. If the user has a custom environment (such as different PATHs to executables or some other customizations), it won't get initialized, so we've to source the initialization file(s) ourselves:

sudo chroot /chroot su - user -c ". ~/.bash_profile; cmd args"

This does what we wanted and runs cmd args inside of chroot located at /new-root-path as user and after running the command it quits and we're back at our shell.

What a beautiful way to combine commands!

On Sourcing ~/.bash_profile

Some people couldn't understand why I was sourcing ~/.bash_profile. The reason is because I was unable to find another way to invoke the login shell and have the environment initialized.

I tried this:

sudo chroot /chroot su -l user -c "cmd args"

And it just wouldn't execute ~/.bash_profile. Su's man page says that -l invokes a login shell, however I don't see that happening. Here's the transcript that demonstrates it:

pkrumins$ pwd
/chroot

pkrumins$ sudo cat ./home/testuser1/.bash_profile 
echo "moo"
PATH="/test/bin"

pkrumins$ sudo chroot /chroot su -l testuser1 -c 'echo $PATH'
/bin:/usr/bin

pkrumins$ sudo chroot /chroot su -l testuser1 
moo

testuser1$ echo $PATH
/test/bin

testuser1$ ^D
logout

pkrumins$ sudo chroot /chroot su -l testuser1 -c '. ~/.bash_profile; echo $PATH'       
moo
/test/bin

pkrumins$ sudo chroot /chroot su -l testuser1
moo

testuser1$ /bin/bash -l -c 'echo $PATH'
moo
/test/bin

This definitely looks like a bug in su to me when -l and -c are used together. So the only way to get the environment loaded is by sourcing the initialization files yourself.

Update

I went through su's source code (can be found in util-linux package) and it turns out it's not a bug in su!

Here are the arguments that su passes to execv when I run su user -c 'echo $PATH' (see run_shell function in su.c):

shell: /bin/bash
args[0]: -bash
args[1]: -c
args[2]: echo $PATH

As you can see, args[0][0] is -, which makes bash a login shell, so bash should be executing the startup files, but it does not!

To figure out what was happening, I built a custom version of bash and added a bunch of debugging statements. I found that there are two different login-shell states! Who'd have thought? One that you get when you've set args[0][0] to -, the other that you get when you've used the -l argument (to bash, not su!).

In case args[0][0] is -, and you're using -c to execute code, bash will not execute startup files because it's in this "non-interactive positive-login-shell" state. However if you use -l and -c, it goes into "non-interactive negative-login-shell" state, and it will execute startup files (see run_startup_files function in bash's shell.c source file).

That was one hell of an adventure debugging this. So if you want bash to execute startup files through su, use my ". ~/.bash_profile" sourcing trick! (Or define NON_INTERACTIVE_LOGIN_SHELLS when building bash).

This article is part of the article series "Bash One-Liners Explained."
<- previous article next article ->

This is the fifth part of the Bash One-Liners Explained article series. In this part I'll teach you how to quickly navigate around the command line in bash using emacs-style keyboard shortcuts. That's right, emacs-style keyboard shortcuts. It might surprise you but by default bash uses emacs-style keyboard shortcuts for navigating around the command line. Keep reading to learn more!

See the first part of the series for introduction. After I'm done with the series I'll release an ebook (similar to my ebooks on awk, sed, and perl), and also bash1line.txt (similar to my perl1line.txt).

Also see my other articles about working fast in bash:

Parts of this post are based on my earlier post Working Productively in Bash's Emacs Command Line Editing Mode. Check it out, too!

And grab the emacs keyboard shortcut cheat sheet!

Let's start.

Part V: Navigating around in emacs mode

0. Introduction to input editing modes

Bash uses the readline library for input editing. The readline library supports emacs style key bindings, vi style key bindings as well as custom key bindings. By default readline will use the emacs style key bindings but you can easily switch to vi editing mode or customize them.

You can switch between the emacs and vi editing modes through set -o emacs and set -o vi commands.

The key bindings can be customized through the ~/.inputrc file or the bind command. For example, bind '"\C-f": "ls\n"' binds CTRL+f to execute ls command. You can learn more about readline's key binding syntax by consulting the readline section of the bash man page.

I'll cover the emacs editing mode in this article. In the next two articles I'll cover the vi editing mode and customizing readline.

1. Move to the beginning of the line

Press Ctrl+a

Ctrl+a moves the cursor to the beginning of the line. Here's an illustration. Let's say you've typed cd info in the terminal:

Before pressing Ctrl+a

Pressing Ctrl+a moves the cursor to the beginning of the line:

After pressing Ctrl+a

2. Move to the end of the line

Press Ctrl+e

Ctrl+e moves the cursor to the end of the line. Here's an illustration. Let's say you've typed mkdir foo bar baz in the terminal and your cursor is somewhere in the middle:

Before pressing Ctrl+e

Pressing Ctrl+e moves the cursor to the end of the line:

After pressing Ctrl+e

3. Move one word backward

Press Esc+b or Alt+b

Esc+b or Alt+b moves the cursor one word backward. You'll often see Meta+b but there is no such key on the keyboards anymore. Therefore either Esc+b or Alt+b will work, depending on how your terminal is configured.

Here's an illustration. Let's say you've typed echo 'hello world' in the terminal and your cursor is after hello:

Before pressing Meta+b

Pressing Esc+b or Alt+b moves the cursor one word backward:

After pressing Meta+b

4. Move one word forward

Press Esc+f or Alt+f

Esc+f or Alt+f moves the cursor one word forward. Here's an illustration. Let's say you've typed echo 'hello world' in the terminal and your cursor is before hello:

Before pressing Meta+f

Pressing Esc+b or Alt+b moves the cursor one word forward:

After pressing Meta+f

5. Delete the last word

Press Ctrl+w

Ctrl+w deletes the last word. Deleting a word is also known as killing a word. Each killed word gets stored in the kill ring buffer. If you accidentally killed a word press Ctrl+y to paste it back. Pasting from the kill ring buffer is also known as yanking.

Here's an illustration. Let's say you've typed cd /foo:

Before pressing Ctrl+w

Pressing Ctrl+w deletes /foo:

After pressing Ctrl+w

6. Paste the deleted word(s) back

Press Ctrl+y

Ctrl+y pastes whatever is in the kill buffer back to the terminal. Here's an illustration. Let's say you had typed cd /foo (see the previous example), and you killed the last word, which is /foo so your command line looks like:

Before pressing Ctrl+y

Pressing Ctrl+y brings /foo back:

After pressing Ctrl+y

7. Move one character backward

Press Ctrl+b

Ctrl+b moves the cursor one char backward. Here's an illustration. Let's say you had typed echo foo bar baz on the command line:

Before pressing Ctrl+b

Pressing Ctrl+b moves the cursor one character backward:

After pressing Ctrl+b

8. Move one character forward

Press Ctrl+f

Ctrl+f moves the cursor one char forward. Here's an illustration. Let's say you moved one character backward (as in the previous example):

Before pressing Ctrl+f

Pressing Ctrl+f moves the cursor one character forward:

After pressing Ctrl+f

9. Delete the whole line

Press Ctrl+u

Ctrl+u kills the whole line and puts it in the kill buffer. Same as with killed words, you can paste the whole line back by pressing Ctrl+y.

Here's an illustration. Let's say you've typed echo moo in your command line:

Before pressing Ctrl+u

Pressing Ctrl+u deletes the whole line:

After pressing Ctrl+u

10. Search the history backward

Press Ctrl+r

This is probably one of the most used keyboard shortcuts in bash. Pressing Ctrl+r searches the command history backwards. For example, you can press Ctrl+r and then type the first few characters of some command that you executed earlier to quickly find it.

Here's an illustration. Let's say you had executed a complicated command such as this one:

joinlines () { sed ':a; N; s/\n/'"$1"'/; ba'; }

And now you wish to modify it but you don't want to keep going through the history to find it. Press Ctrl+r and type something you remember from the command like joi:

After pressing Ctrl+r

11. Search the history forward

Press Ctrl+s

If you press Ctrl+s, most likely your terminal will freeze because by default your terminal interprets Ctrl+s as the stop-flow signal. When I was less experienced this was driving me crazy. I'd accidentally press Ctrl+s and my terminal would freeze. And I had no idea what was happening. Later I learned that I can press CTRL+q to unfreeze the terminal (Ctrl+q starts the flow again.)

The right way to go is to change the terminal behavior for Ctrl+s via the stty command:

$ stty stop 'undef'

This will undefine the key binding for the stop-flow signal and you'll be able to use bash's Ctrl+s.

Ctrl+s comes handy when you've searched too far with Ctrl+r. Then you can just simply reverse the search direction by pressing Ctrl+r.

Here's an illustration. Let's say you typed awk and pressed Ctrl+r a few times and you skipped past the awk command that you wanted to find:

Before pressing Ctrl+s

Pressing Ctrl+s reverses the history search direction:

After pressing Ctrl+s

12. Exchange two adjacent characters quickly

Press Ctrl+t

Ctrl+t transposes two characters (exchanges them) and moves the cursor one character forward. Here's an illustration. Let's say you've mistyped echo in ehco bar baz and your cursor is at the letter c:

Before pressing Ctrl+t

Pressing Ctrl+t exchanges c with h and moves the cursor one char forward:

After pressing Ctrl+t

13. Exchange two adjacent words quickly

Press Esc+t or Alt+t

Esc+t or Alt+t transposes two words (exchanges them) and moves the cursor one word forward. Here's an illustration. Let's say you've typed foo bar baz and your cursor is at the word bar:

Before pressing Meta+t

Pressing Esc+t or Alt+t exchanges foo with bar and moves the cursor one word forward:

After pressing Meta+t

14. Uppercase the rest of the word

Press Esc+u or Alt+u

Esc+u or Alt+u uppercases the rest of the word. Here's an illustration. Let's say you've typed foo bar baz and your cursor is at the beginning of bar:

Before pressing Meta+u

Pressing Esc+u or Alt+u uppercases the whole word and bar becomes BAR:

After pressing Meta+u

15. Lowercase the rest of the word

Press Esc+l or Alt+l

Esc+t or Alt+t uppercases the rest of the word. Here's an illustration. Let's say you've typed foo BAR baz and your cursor is at the beginning of BAR:

Before pressing Meta+l

Pressing Esc+l or Alt+l lowercases the whole word and BAR becomes bar:

After pressing Meta+l

16. Capitalize a word

Press Esc+c or Alt+c

Esc+c or Alt+c properly capitalizes a word. Here's an illustration. Let's say you've typed foo bar baz and your cursor is at the beginning of bar:

Before pressing Meta+c

Pressing Esc+c or Alt+c capitalizes the first letter of the word and bar becomes Bar:

After pressing Meta+c

17. Insert a raw character (such as TAB or Ctrl+c)

Press Ctrl+v

Ctrl+v inserts the next character typed verbatim. For example, Ctrl+v followed by <TAB> would insert a literal tab in the command line, or Ctrl+v followed by Ctrl+m would insert a Windows newline (aka carriage return CR).

Here's an illustration. Let's say you've typed echo foo:

Before pressing Ctrl+v

Pressing Ctrl+v followed by Ctrl+m inserts a literal Ctrl+m:

After pressing Ctrl+v Ctrl+m

18. Comment the current line (append # at the beginning quickly)

Press Esc+# or Alt+#

Esc+# or Alt+# quickly comments the line. Here's an illustration. Let's say you typed echo foo bar baz:

Before pressing Meta+#

Pressing Ctrl+# inserts the comment symbol # at the beginning of the line:

After pressing Meta+#

19. Open the current command in a text editor quickly

Press Ctrl+x, Ctrl+e

Pressing CTRL+x followed by CTRL+e opens the current command in your favorite text editor. Exiting the editor will execute the command.

20. Delete a character to the left

Press Ctrl+h

Ctrl+h deletes the character to the left of the cursor. Here's an illustration. Let's say you've typed echo qwerty:

Before pressing Ctrl+h

Pressing Ctrl+h deletes the character to the left:

After pressing Ctrl+h

21. Delete a character to the right

Press Ctrl+d

Ctrl+d deletes the character to the right of the cursor. Here's an illustration. Let's say you've typed echo qwerty:

Before pressing Ctrl+d

Pressing Ctrl+d deletes the character to the right:

After pressing Ctrl+d

22. Incremental undo

Press Ctrl+x, Ctrl+u

Pressing Ctrl+x followed by Ctrl+u undoes a change. Here's an illustration. Let's say you typed foo bar baz and then deleted baz and typed moo:

Before pressing Ctrl+x Ctrl+u

Pressing Ctrl+x, Ctrl+u a few times undoes the last changes and you end up with foo bar baz again:

After pressing Ctrl+x Ctrl+u

23. Insert the last argument from the previous command

Press Esc+. or Alt+.

Esc+. or Alt+. inserts the last argument from the previous command at the current cursor position. Here's an illustration. Let's say you had run ls archive.tgz:

Before pressing Meta+.

And now you want to extract the archive. So all you've to do is type tar -xzf and press Esc+. or Alt+.:

Before pressing Meta+.

24. Undo all changes to the line

Press Esc+r or Alt+r

Esc+r or Alt+r undoes all changes to the line. It's useful when you're going through the command history with Ctrl+r and make changes. If you mess up, you can quickly revert back to the original command by pressing Esc+r or Alt+r.

Here's an illustration. Let's say you searched for grep in history:

Before pressing Meta+r

And let's say you wanted to make some changes to the command but messed up:

Before pressing Meta+r

Pressing Esc+r or Alt+r undoes all the changes and you end up with the original grep command that was in the history:

After pressing Meta+r

25. Clear the screen

Press Ctrl+l

Ctrl+l clears the screen. Alternatively you can type reset.

26. Change input mode to vi

$ set -o vi

This command changes the key bindings to vi's. If vi's your favorite editor, you'll love this. I'll cover the vi mode in more details in the next part of the article.

Cheat Sheet

I made a cheat sheet that lists all the default emacs mode keyboard shortcuts. Download the emacs keyboard shortcut cheat sheet.

Enjoy!

Enjoy the article and let me know in the comments what you think about it!

I thought I'd share my favorite regex of all time:

[ -~]

Any ideas what this regexp matches?

It matches all ASCII characters from the space to the tilde. What are those characters? They're all printable characters!

Take a look at the ASCII table. The printable characters start at the space and end at the tilde:


[ -~] matches all printable ascii characters (light blue background)

I love this.

Update:

Would you like to wear this regular expression? I just had an idea to make it a t-shirt as this blog post is getting so popular. Check out this t-shirt that I made:

This shirt is available through Teespring. Teespring is like Kickstarter for t-shirts. If 50 people buy the shirt, everyone gets their shirt. Otherwise no one gets charged and no one gets the shirt.

This month I'm presenting the limited-edition node.js duplex stream tees designed by the stream master and node.js wizard SubStack.

This shirt is done in collaboration with Teespring. Teespring is basically Kickstarter for t-shirts. If enough people commit to buying a shirt, everyone gets their shirt. Otherwise nobody gets a shirt (and nobody gets charged).

This tee illustrates the duplex stream pattern a.pipe(b).pipe(a) that sends the messages around like a telephone.

The shirt is available on Hanes tees and is priced at $15.

Get Your Limited Edition Duplex Stream T-Shirt Now!

Enjoy!

Do you love this shirt and want to get more geek shirts in the future? Subscribe to my blog and follow me on twitter for updates!

I just remembered that I had forgotten the ASCII trick to convert lowercase to uppercase and back. The trick is super simple, you xor the 6th bit (or 5th depending on how you count bits) and that changes the case!

Check this out:

a = 01100001
A = 01000001

See, just the 6th bit changed.

Why is it this way? Simply because the people who invented ASCII thought it was a great idea. If you look at characters a..z, you'll see that all of them have the 6th bit set to 1. The ASCII inventors though, hey, let's set 6th bit to 0 for upper case letters A..Z, then it will be super easy to change case. So they did.

a = 01100001    A = 01000001 
b = 01100010    B = 01000010 
c = 01100011    C = 01000011 
d = 01100100    D = 01000100 
e = 01100101    E = 01000101 
f = 01100110    F = 01000110 
g = 01100111    G = 01000111 
h = 01101000    H = 01001000 
i = 01101001    I = 01001001 
j = 01101010    J = 01001010 
k = 01101011    K = 01001011 
l = 01101100    L = 01001100 
m = 01101101    M = 01001101 
n = 01101110    N = 01001110 
o = 01101111    O = 01001111 
p = 01110000    P = 01010000 
q = 01110001    Q = 01010001 
r = 01110010    R = 01010010 
s = 01110011    S = 01010011 
t = 01110100    T = 01010100 
u = 01110101    U = 01010101 
v = 01110110    V = 01010110 
w = 01110111    W = 01010111 
x = 01111000    X = 01011000 
y = 01111001    Y = 01011001 
z = 01111010    Z = 01011010 

Also check this out, if you xor a character with a space, you invert the case:

$ perl -le 'print "a"^" "'   
A
$ perl -le 'print "A"^" "' 
a

Why is that? Because a space ' ' has the value 32, which is 1<<5, which is the 6th bit, which swaps the case!

Bonus

Here are the commands I used for this blog post:

$ perl -e 'printf "%08b\n", ord("K")'
$ perl -e 'printf "%s = %08b    %s = %08b\n", $_, ord, uc, ord uc for a..z' 

Simple articles best articles.