
Advancing in the Bash Shell - yarapavan
http://samrowe.com/wordpress/advancing-in-the-bash-shell/
======
kbenson
I don't use !!, or !$, because I prefer to see what command is being executed
_before_ I press enter. For the same reason, I will _never_ use !word. But
that's not a problem, because as others have noted bash makes it easy to not
have to fall back to these.

Alt-. will generally add the prior last argument into the current command, and
repeated presses will cycle through previous commands.

Ctrl-r will dynamically update to show you the prior command matched as you
start typing afterwards, so you can confirm it's correct.

Both are essential if you want to reuse prior commands but either can't trust
that you were the last person using the shell, or don't want a typo to ruin
your day, and possibly many other people's day as well. That is, if you're a
sysadmin using the automatic variants, I don't want you anywhere near systems
I rely on, I want you far away. Perhaps working for a competitor.

~~~
benvd
>I don't use !!, or !$, because I prefer to see what command is being executed
before I press enter.

I use zsh (with oh-my-zsh) and when I type e.g. "sudo !!" and press enter, it
doesn't execute the command, instead it expands the !!. Another enter will
then actually execute it.

I believe it's default behavior for oh-my-zsh. At least, I don't recall
manually changing this.

~~~
jandrese
In zsh you can type !!<tab> and it will auto-expand the line for you. I find
histverify to be kind of annoying so I don't use it.

~~~
Something1234
How so?

------
pmoriarty
I virtually never use !! any more. Instead I just type control-r followed by a
portion of the command in my history that I'm interested in.

That's a much more interactive way of getting to the command I want, and if
the first match isn't what I want I can either keep typing more of the command
or type control-r for the next match. I can also type control-s to go to the
previous match. For that last trick to work, you need to do something like
this first: stty stop ""

Granted, I use zsh, but I think it works pretty much the same in bash.

Another trick I really like, which I've only used in zsh, but which might
exist in bash too (since bash and zsh have so much feature parity these days)
is to bind a keystroke to "insert-last-word". When this keystroke is hit, it
appends the last argument of the previous command to the end of the current
line, further typing of this same keystroke will remove the appended argument
and instead append the last argument of the previous command, and so on. I use
this _all the time_ and therefore never have to type !$

And my favorite shell trick of all time: set -o vi

~~~
Myrmornis
Agreed, but even nicer than control-r are the readline functions `history-
search-backward` and `history-search-forward`.

Personally I bind them to up/down which on OS X at least involves:

    
    
      # Put this in some file like ~/.readline-bindings
      "\e[A": history-search-backward
      "\e[B": history-search-forward
    
      # And this in your ~/.bashrc or ~/.zshrc
      bind -f ~/.readline-bindings
    

That way if you haven't typed any input it behaves like normal up (previous
command), but if you've typed some characters it only retrieves matching
commands.

[http://codeinthehole.com/writing/the-most-important-
command-...](http://codeinthehole.com/writing/the-most-important-command-line-
tip-incremental-history-searching-with-inputrc/)

~~~
devnonymous
Just curious since I don't use a mac, doesn't writing the mappings to .inputrc
work? IIRC, putting your readline bindings there makes it available for all
readline enabled prompts, not just bash ie: Python, pgsql, mysql,.. Etc

~~~
Myrmornis
Yes I think that's a better approach, thanks. I don't know why I started using
`bind` in my shell config.

------
noobermin
I wonder how many people know a few of these (like ctrl-a and ctrl-e and other
navigation commands) come not from bash per se but from readline[0]. It isn't
a surprise, then, that these mirror emacs commands. For example, one I use
often is ctrl-k to kill to the end of a line...and, yes, just like emacs, you
can yank it back with ctrl-y too :)...I often do this if I type out a huge
command, forget that I needed to type another before, so I use that to "save"
commands before execution. That is, it's essentially cheap copy and paste on
the commandline.

Cherry on top? These work on other readline using tools! They work in the
interactive repl's like python, node, irb, ghcim the exact same shortcuts...
it makes working with repl's much more pleasant for me.

[0]
[https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html](https://cnswww.cns.cwru.edu/php/chet/readline/rltop.html)
[1] quick note, node might use a readline like but it doesn't support yanking
(pasting) unfortunately.

------
dorfsmay
I consider this misinformation and makes my blood boil. I feel like the author
learn partial information and decided to write a blog about without
researching further...

ctrl-R is not bash, it's GNU readline.

[http://cnswww.cns.cwru.edu/php/chet/readline/rluserman.html](http://cnswww.cns.cwru.edu/php/chet/readline/rluserman.html)

If you are going to use command line, than absolutely you should use command
history editing, but why limiting yourself to a few commands learned through
osmosis or haphazardly rather than use a full editor? Why use C-r to search
backwards, but not M-backspace to delete an entire word?

When I explain this, people usually argue that "there are two many weird
commands to learn, and I only need the few I know". The same type of people
often go on a rant about how much vim is so much better than emacs.

There are not weird command to learn, none, zero!

Use your preferred editor! You already know it, you already know all those
commands, and if you don't buy a book and learn them! You know and like emacs?
Great, that's the default in readline. Go ahead C-r, C-backspace, C-a, C-e,
M-f all you need. You already know all of those.

But you don't like emacs, or don't even know that it exists beside the fact
that it's something fun to shit on? You think you're a big shot because you
know vim really well? Not an issue, type the magic command "set -o vi" and you
are now in vi mode, you can esc-/ to search, esc-k to call the last command,
and once editing a command, cw to change word, $ to go to the end of the line,
etc...

I do not buy "I'm a vim person, but I just use ctrl-R and the arrows", because
you are basically limiting yourself just because you had not realised readline
is a full editor. You have nothing to learn, just set the editor mode in your
preferred editor mode, and start using it, trust me, you will start getting
use to it and use its power very quickly.

BONUS: It' snot just bash! Any program that use readline can do the same! psql
etc... use it (or a something like readline, because of GPL vs non-gpl
etc...).

BONUS 2: You can use readline with software that don't use it, it's call
rlwrap. Go ahead, use "rlwrap sqlplus" once, then try to use oracle without
it!

~~~
dllthomas
> ctrl-R is not bash, it's GNU readline

As readline was codeveloped with bash, I think it's incorrect to say it's
_not_ bash. It is bash _by way of_ readline - additional information important
for some contexts, not for others.

> cw to change word, $ to go to the end of the line, etc...

But not, sadly, ci" to change what's inside quotes.

\----

As to your obviously troubling experiences with some vim users, I apologize on
behalf of my sect. But tone down your characterizations - there are plenty of
smug and superior emacs users as well. (For my part, I don't much care what
others use as long as they actually learn their way around some sort of
powerful editor.)

~~~
dorfsmay
I'm a vim person myself. my issue isn't with vim vs emacs, but that people who
choose vim as their editor don't switch their command line editing to vim
mode, and find it both worse and ironic when it comes to the subset of vim
users who are vocal against emacs!

~~~
dllthomas
I think there's a fair amount of room for a workflow that does most meaningful
commandline manipulation inside the editor, in which case leaving vim in emacs
mode and "just" knowing C-r and C-x C-e could be sort of reasonable.

I certainly understand being skeptical of "vim modes" \- they are often
incomplete in painful ways. As mentioned above, I often run up against the
limitations of bash vim mode when trying to change what's inside quotes.

------
js8
Another very useful feature related to brace expansion are sequences:

{1..99} will give 1 2 3 4 5 .. 9 10 11 12 .. 98 99

{01..99} will give 01 02 03 04 05 .. 09 10 11 12 .. 98 99

And so on. I think it also works with letters.

~~~
FoeNyx
> I think it also works with letters.

Indeed it does. According to the man page : "When characters are supplied, the
expression expands to each character lexicographically between x and y,
inclusive, using the default C locale."

$ echo {a..z}

a b c d e f g h i j k l m n o p q r s t u v w x y z

But it does not seem to work with ranges outside ASCII such as "à..é", "あ..お",
or smileys range, even though my locale should be ".UTF-8" compliant.

Also, you can specify an increment value :

$ echo {0..42..7}

0 7 14 21 28 35 42

$ echo {z..a..3}

z w t q n k h e b

~~~
mturmon
Thanks.

And as your last example illustrates, downward ranges are automatically
invoked:

    
    
      $ echo {10..1}
      10 9 8 7 6 5 4 3 2 1
    

As are negative endpoints (shown below with some automatic number formatting,
which is turned on when either the first or the second endpoint is padded):

    
    
      $ echo {2..-002}
      0002 0001 0000 -001 -002
    

I just noticed that all this is documented behavior -- another illustration of
how rococo this shell has become.

------
jkot
My best progress in shell was by switching to Fish. Much more intuitive for
programmer. Guess what this does:

    
    
      while true
        clear
        df
        sleep 1
      end

~~~
laurent123456
Interestingly, none of the two Bash versions in the comments are equivalent to
your code, which kind of prove your point with Bash being tricky. `while sleep
1` means the script will sleep once before running `df`, while it should only
sleep after. The other command also won't work because it's missing colons
between commands.

~~~
INTPenis
The point I wanted to make was that OP sacrificed compatibility with all major
distros for being rid of "; do", essentially.

I just don't see the point. Bash might have flaws, nothing is perfect, but
it's one vital piece of software that most distros have installed.

------
ashayh
Do you use Ctrl+R a lot? You should give fzf a try:
[https://github.com/junegunn/fzf](https://github.com/junegunn/fzf)

It works for both Vim & Bash.

------
0xmohit
Another good read:

Bash Shortcuts For Maximum Productivity [0]

[0] [http://www.skorks.com/2009/09/bash-shortcuts-for-maximum-
pro...](http://www.skorks.com/2009/09/bash-shortcuts-for-maximum-
productivity/)

------
JoshTriplett
One more handy one, when you have a complex command to deal with: C-x C-e will
open up the current command line in your text editor, and when you save and
quit, it'll run the line. So if you want to do some complex editing operation
on the command line, you can do so with your preferred editor.

------
hibbelig
I know of "M-." which will insert the last word of the previous command, and
typing "M-." again will replace it with the last word of the command before
that.

Is there something that goes through the words, instead? So I hit a key, it
inserts the last word of the previous command, then I hit the key again and it
replaces it with the penultimate word of the previous command?

I think using bang, I can do "!-3:1" to get the second word of the command
three lines up?

------
adamnemecek
> Some people hold the belief that using a GUI is faster than using a CLI.

I've heard more people say this than that GUI is faster. Also it really
depends on what you are doing.

~~~
pjmlp
I rather use a REPL environment that combines GUI and CLI, like in Xerox and
ETHZ OSes.

Think of DrRaket, Swift Playgrounds or Jupyter like experience.

~~~
contextfree
Yes, there's far too little of this. I'd love something that combines the good
points of Powershell and a GUI file explorer for example. Even the primitive
out-gridview or ogv is incredibly handy.

~~~
pjmlp
Powershell is probably the closest we have currently in terms of that
experience.

------
gavinpc
Regarding

    
    
        alias ssh=’ssh -AX’
        alias ls=’ls –color=auto’
    

Let me quote the Mercurial manual:

> It is possible to create aliases with the same names as existing commands,
> which will then override the original definitions. This is almost always a
> bad idea![0]

I was recently bitten by this when running a build on a new machine. I was
doing a

    
    
        log -r "$revision"
    

and instead of getting one commit message (as usual), I was getting the entire
log history up to $revision.

I managed to find that Mercurial had made a breaking change[1] so that `log
-fr` would now have the observed behavior. But I was only running `log -r`!

But on the old machine, I had in my .hgrc the alias

    
    
        log=log -f 
    

To top it off, I also had a comment with the above quote that this is "almost
always a bad idea!" Consider me burned!

[0]
[https://www.selenic.com/mercurial/hgrc.5.html#alias](https://www.selenic.com/mercurial/hgrc.5.html#alias)

[1] [https://www.mercurial-
scm.org/wiki/UpgradeNotes#A3.4:_minor_...](https://www.mercurial-
scm.org/wiki/UpgradeNotes#A3.4:_minor_changes)

~~~
1amzave
Also (more specifically to one of the given examples), enabling `-A` on _all_
your ssh invocations likewise seems like a pretty bad idea.

------
aabajian
Given the title of the article, I was really hoping that the author would
answer the following question. Suppose I have the following line:

>rsync ~/20160604/apache-tomcat-7.0.63/webapps/ROOT/contact.html apache-
tomcat-1:/svr/apache-tomcat-7.0.63/webapps/ROOT/

I'm rsyncing the contact.html file to my website. Now I want to rsync the
index.html page. I do this by:

1\. Pressing the up arrow key to get the previous command.

2\. Press and hold the back arrow key until it gets to "contact.html"

3\. Press and hold backspace to delete "contact.html" and type "index.html"

My question is: How to quickly move the cursor to a location on the line. For
example, in Vi I could do "F+c" twice to move to the first "c" if this were a
text file. I know I can enabled Vi commands for Bash, but this seems
cumbersome. There has to be a way in Bash to jump around the already typed
line.

~~~
lolive
Yet another option:

Typing "Ctrl-r aString" will recall the previous command and position the
cursor where the string "aString" occurs. You can then Alt-d, Ctrl-w,
whatever.

cf [http://stackoverflow.com/a/2215511](http://stackoverflow.com/a/2215511)

~~~
aabajian
Oh hey, this is definitely the closest / fastest to what I want. Although if I
cut and paste a line from another window, I still need a way to jump to some
position in that line.

~~~
lolive
Last resort is Ctrl-x Ctrl-e :)

------
partycoder
I use fish. It's not a popular choice of a shell, but the autosuggestions
feature saves a LOT of time.

~~~
ilaksh
I use fish also. Its more popular than people admit because it makes things
easier.

------
gitaarik
Did you know Ctrl + / is like undo while composing a command? So when you for
example delete a word with Ctrl + W, you can get it back with Ctrl + /.

------
INTPenis
History manipulation in bash only reminds me why I have both the current
commands history number and the last return code in my prompt.

I don't need colors, or anything fancy in my prompt, but those two things I
find to be really useful.

    
    
      export PS1='[\u@\h \W](\!/$?)$ '

------
Mojah
So happy to see this article get traction, it truly deserved it! Many unknown
Bash tricks!

I featured it in the cron.weekly newsletter 2 weeks ago:
[https://www.cronweekly.com/issue-44/](https://www.cronweekly.com/issue-44/)

------
lolive
I like this article a lot. A lot of funny tricks to try and learn.

A missing piece of information IMHO: Ubuntu now maps a nice readline features
to PgUp and PgDn: history-search-backward and history-search-forward.

Type the beginning of a command line, and with PgUp-PgDn, you will traverse
your history for the command lines beginning with what you have typed.

That's a no-brainer in my Bash life.

Note: to enable that feature in your Bash, here is a tutorial:
[http://dqxtech.net/blog/2011-03-06/linux-bash-history-
pgup-p...](http://dqxtech.net/blog/2011-03-06/linux-bash-history-pgup-pgdown)

------
yarapavan
Related: BASH FAQ -
[http://mywiki.wooledge.org/BashFAQ](http://mywiki.wooledge.org/BashFAQ)

Also, look at the screen tips by the same author -
[http://samrowe.com/wordpress/2010/06/16/gnu-screen-quick-
tip...](http://samrowe.com/wordpress/2010/06/16/gnu-screen-quick-tips/)

------
aoloe
I almost never use any of the "shortcuts" explained. And I wonder if it would
be worth learning them. They look much more cumbersome than the alternatives,
I use in my everyday work.

I have to admit, that -- at some time in the past -- I've even tried to
activate vim mode. As a heavy vim user i thought it would make sense.

At the end I'm stuck with the Emacs-inspired (is it really Emacs?) shortcuts.

They seem to be the ones that make most sense in the short living, very
interactive, shell command line:

\- Arrows for going up/down one or two commands,

\- ctrl-r for interactive searching in the history,

\- ctrl-a for going back to the beginning of the row,

\- ctrl-e to the end,

\- del & and backscape for deleting a bunch of chars,

\- in some case ctrl-w for deleting words backwards (mostly for deleting the
url in the latest wget command),

\- and, of course, tab completion.

When I'm in need for more complex interactions:

\- I start vim and

\- work with a mix of !! commands (shell that outputs the result in the
current vim buffer),

\- vim manipulations (vertical selections, ctrl-x-f, qq short living macros),
A and I with a . repeat),

\- join everything in a row and yank it and, finally,

\- a :!ctrl-r" to run it.

When the interaction is even more complex, than it's time for a shell script.

Last resort being a Python script.

Now, why would I take the time to write `rm my-file{-01,-02,old}` and risk
getting a typo into it, instead of relying on the tab-autocomplete?

~~~
wtetzner
> Now, why would I take the time to write `rm my-file{-01,-02,old}` and risk
> getting a typo into it, instead of relying on the tab-autocomplete?

If you write `rm my-file{-01,-02,old}` and then press tab, it will expand it
for you before you run it.

~~~
greggyb
I've tried this and I don't get any expansion on tab. Is this a Bash or Zsh
feature?

~~~
wtetzner
Hmm, could be zsh.

------
foota
I didn't realize that you could do something like: vim <(find . | grep
coolfile) until just the other week, pretty dang useful.

~~~
bgaluszka
I often use `git grep something | vim -` which puts list of files into vim
with line numbers, and then I do `gF` to get to file and particular line.

~~~
foota
I didn't realize vim supported reading from standard in, thanks!

~~~
dllthomas
You can go one further. I have an alias 'cbuf=vim - -ccbuf!'

This takes whatever is piped in and loads it into the quickfix list. So I get
locations out of anything with lines of the format "file:line: something" or
"file:line:col: something".

Eg: git grep -n | cbuf

------
david90
[Danger, do not try to run in your system] I like this :(){ :|: & };: which is
a fork bomb [http://askubuntu.com/questions/159491/why-did-the-command-
ma...](http://askubuntu.com/questions/159491/why-did-the-command-make-my-
system-lag-so-badly-i-had-to-reboot)

~~~
ansgri
How do you, as a root, protect against it? It's unseemly that a non-root can
DoS a system.

~~~
david90
Should set user process limitations, in /etc/security/limits.conf

------
noisy_boy
Tip for "set -o vi" users: when you have issued a long multiline command and
your line wrapping isn't working very well, Esc-k until you get to the command
and then press v - this will open your command in vi and you can now edit it
more easily. Quitting with :wq will execute your command.

~~~
dllthomas
Strictly it will open your command in $EDITOR, but if you've "set -o vi"
that's probably vi/vim/neovim.

Outside vi mode, C-x C-e does the same thing.

It's "edit-and-execute-command" in readline. Incidentally, this really does
what it says on the tin: if you use "read -e" to enable readline support at a
prompt, and you try to edit what you're inputting this way, it won't pass the
result into the read - it will try to execute it.

------
fphilipe
I used to do !$ a lot until I configured iTerm to allow me to cycle through
the last args with alt-. which is way faster. But I did not know about the
colon modifiers, so !$ might make its way back into my workflow. ZSH gives me
a nice overview of what's possible:

    
    
        > !$:<tab>
        &  -- repeat substitution
        A  -- absolute path resolving symbolic links
        Q  -- strip quotes
        a  -- absolute path
        c  -- PATH search for command
        e  -- leave only extension
        g  -- globally apply s or &
        h  -- head - strip trailing path element
        l  -- lower case all words
        q  -- quote to escape further substitutions
        r  -- root - strip suffix
        s  -- substitute string
        t  -- tail - strip directories
        u  -- upper case all words

------
rbonvall
A trick I used to use in the floppy disk era:

    
    
        $ mount /mnt/floppy
        $ mv !$/file_I_needed .
        $ u!mo
    

The last command would umount(8) the floppy.

I use !! all the time, specially when first using find to see which files I
will operate on and then doing the actual operation:

    
    
        $ find . -name '*Foo*.scala'
        path/src/main/scala/foo/Foo.scala
        path/src/test/scala/foo/FooSpec.scala
        $ git add "$(!!)"
    

Shameless plug: I actually use nq, my find wrapper:
[https://github.com/rbonvall/nq](https://github.com/rbonvall/nq)

------
ramses0
My favorite tip in this vein is "fc" => Fix Command, along with "#my_command
--here".

If you're unsure of a command, press insert a leading "#" to make it a comment
(Up-Arrow, Ctrl-A, #, Enter). Then "fc" will load that command into $EDITOR
where you can have full text edit capabilities before writing and exiting the
file to execute the command.

I know there's some magic keystrokes to get into vi-mode from the command line
but I can never remember them, whereas "fc" is pretty easy to remember for the
rare occasion when I need it.

~~~
dllthomas
The magic keystrokes are whatever fires the "edit-and-execute-command"
command.

If you're in emacs mode, it's C-x C-e. Mnemonic: e for editor, x because it's
emacs and lots of stuff starts with C-x

If you're in vi mode, it's v from normal mode, so if you're in insert mode
it's escape, then v. Mnemonic: v for vi

------
falcolas
One that I love that is missing:

    
    
        M-# (i.e. alt-shift-3)
    

Comments the line currently being edited and places it at the top of history.
Great when searching back through history and editing a line safely before
executing it.

------
selfsimilar
One note I wish the author would add is that ^a to move to the beginning of a
line often conflicts with _screen_ or _tmux_ as a command prefix, so I tend to
use ^x^x as a replacement.

------
gravypod
I wouldn't ever day that the GUI is the fastest way to interact with a
computer. It's just the safest and simplest.

Those are usually more important then speed.

------
giosch
I thought that i knew more than i actually know. Thanks!

------
ilaksh
Bash has lots of cool features, but zsh has more and better defaults, and fish
is even better.

------
rajangdavis
This is tight. Thanks for sharing, the history portion at the beginning is
really cool.

------
_ZeD_
"Some people hold the belief that using a GUI is faster than using a CLI."

wha?

------
Zardoz84
^a I would remember it

