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

I love working in the shell. Mastery of shell lets you get things done in seconds, rather than minutes or hours, if you chose to write a program instead.

In this article I'd like to explain the top one-liners from the commandlinefu.com. It's a user-driven website where people get to choose the best and most useful shell one-liners.

But before I do that, I want to take the opportunity and link to a few of my articles that I wrote some time ago on working efficiently in the command line:

Update: Russian translation now available.

And now the explanation of top one-liners from commandlinefu.

#1. Run the last command as root

$ sudo !!

We all know what the sudo command does - it runs the command as another user, in this case, it runs the command as superuser because no other user was specified. But what's really interesting is the bang-bang !! part of the command. It's called the event designator. An event designator references a command in shell's history. In this case the event designator references the previous command. Writing !! is the same as writing !-1. The -1 refers to the last command. You can generalize it, and write !-n to refer to the n-th previous command. To view all your previous commands, type history.

This one-liner is actually really bash-specific, as event designators are a feature of bash.

I wrote about event designators in much more detail in my article "The Definitive Guide to Bash Command Line History." The article also comes with a printable cheat sheet for working with the history.

#2. Serve the current directory at http://localhost:8000/

$ python -m SimpleHTTPServer

This one-liner starts a web server on port 8000 with the contents of current directory on all the interfaces (address 0.0.0.0), not just localhost. If you have "index.html" or "index.htm" files, it will serve those, otherwise it will list the contents of the currently working directory.

It works because python comes with a standard module called SimpleHTTPServer. The -m argument makes python to search for a module named SimpleHTTPServer.py in all the possible system locations (listed in sys.path and $PYTHONPATH shell variable). Once found, it executes it as a script. If you look at the source code of this module, you'll find that this module tests if it's run as a script if __name__ == '__main__', and if it is, it runs the test() method that makes it run a web server in the current directory.

To use a different port, specify it as the next argument:

$ python -m SimpleHTTPServer 8080

This command runs a HTTP server on all local interfaces on port 8080.

#3. Save a file you edited in vim without the needed permissions

:w !sudo tee %

This happens to me way too often. I open a system config file in vim and edit it just to find out that I don't have permissions to save it. This one-liner saves the day. Instead of writing the while to a temporary file :w /tmp/foobar and then moving the temporary file to the right destination mv /tmp/foobar /etc/service.conf, you now just type the one-liner above in vim and it will save the file.

Here is how it works, if you look at the vim documentation (by typing :he :w in vim), you'll find the reference to the command :w !{cmd} that says that vim runs {cmd} and passes it the contents of the file as standard input. In this one-liner the {cmd} part is the sudo tee % command. It runs tee % as superuser. But wait, what is %? Well, it's a read-only register in vim that contains the filename of the current file! Therefore the command that vim executes becomes tee current_filename, with the current directory being whatever the current_file is in. Now what does tee do? The tee command takes standard input and write it to a file! Rephrasing, it takes the contents of the file edited in vim, and writes it to the file (while being root)! All done!

#4. Change to the previous working directory

$ cd -

Everyone knows this, right? The dash "-" is short for "previous working directory." The previous working directory is defined by $OLDPWD shell variable. After you use the cd command, it sets the $OLDPWD environment variable, and then, if you type the short version cd -, it effectively becomes cd $OLDPWD and changes to the previous directory.

To change to a directory named "-", you have to either cd to the parent directory and then do cd ./- or do cd /full/path/to/-.

#5. Run the previous shell command but replace string "foo" with "bar"

$ ^foo^bar^

This is another event designator. This one is for quick substitution. It replaces foo with bar and repeats the last command. It's actually a shortcut for !!:s/foo/bar/. This one-liner applies the s modifier to the !! event designator. As we learned from one-liner #1, the !! event designator stands for the previous command. Now the s modifier stands for substitute (greetings to sed) and it substitutes the first word with the second word.

Note that this one-liner replaces just the first word in the previous command. To replace all words, add the g modifer (g for global):

$ !!:gs/foo/bar

This one-liner is also bash-specific, as event designators are a feature of bash.

Again, see my article "The Definitive Guide to Bash Command Line History." I explain all this stuff in great detail.

#6. Quickly backup or copy a file

$ cp filename{,.bak}

This one-liner copies the file named filename to a file named filename.bak. Here is how it works. It uses brace expansion to construct a list of arguments for the cp command. Brace expansion is a mechanism by which arbitrary strings may be generated. In this one-liner filename{,.bak} gets brace expanded to filename filename.bak and puts in place of the brace expression. The command becomes cp filename filename.bak and file gets copied.

Talking more about brace expansion, you can do all kinds of combinatorics with it. Here is a fun application:

$ echo {a,b,c}{a,b,c}{a,b,c}

It generates all the possible strings 3-letter from the set {a, b, c}:

aaa aab aac aba abb abc aca acb acc
baa bab bac bba bbb bbc bca bcb bcc
caa cab cac cba cbb cbc cca ccb ccc

And here is how to generate all the possible 2-letter strings from the set of {a, b, c}:

$ echo {a,b,c}{a,b,c}

It produces:

aa ab ac ba bb bc ca cb cc

If you liked this, you may also like my article where I defined a bunch of set operations (such as intersection, union, symmetry, powerset, etc) by using just shell commands. The article is called "Set Operations in the Unix Shell." (And since I have sets in the shell, I will soon write articles on on "Combinatorics in the Shell" and "Algebra in the Shell". Fun topics to explore. Perhaps even "Topology in the Shell" :))

#7. mtr - traceroute and ping combined

$ mtr google.com

MTR, bettern known as "Matt's Traceroute" combines both traceroute and ping command. After each successful hop, it sends a ping request to the found machine, this way it produces output of both traceroute and ping to better understand the quality of link. If it finds out a packet took an alternative route, it displays it, and by default it keeps updating the statistics so you knew what was going on in real time.

#8. Find the last command that begins with "whatever," but avoid running it

$ !whatever:p

Another use of event designators. The !whatever designator searches the shell history for the most recently executed command that starts with whatever. But instead of executing it, it prints it. The :p modifier makes it print instead of executing.

This one-liner is bash-specific, as event designators are a feature of bash.

Once again, see my article "The Definitive Guide to Bash Command Line History." I explain all this stuff in great detail.

#9. Copy your public-key to remote-machine for public-key authentication

$ ssh-copy-id remote-machine

This one-liner copies your public-key, that you generated with ssh-keygen (either SSHv1 file identity.pub or SSHv2 file id_rsa.pub) to the remote-machine and places it in ~/.ssh/authorized_keys file. This ensures that the next time you try to log into that machine, public-key authentication (commonly referred to as "passwordless authentication.") will be used instead of the regular password authentication.

If you wished to do it yourself, you'd have to take the following steps:

your-machine$ scp ~/.ssh/identity.pub remote-machine:
your-machine$ ssh remote-machine
remote-machine$ cat identity.pub >> ~/.ssh/authorized_keys

This one-liner saves a great deal of typing. Actually I just found out that there was a shorter way to do it:

your-machine$ ssh remote-machine 'cat >> .ssh/authorized_keys' < .ssh/identity.pub

#10. Capture video of a linux desktop

$ ffmpeg -f x11grab -s wxga -r 25 -i :0.0 -sameq /tmp/out.mpg

A pure coincidence, I have done so much video processing with ffmpeg that I know what most of this command does without looking much in the manual.

The ffmpeg generally can be descibed as a command that takes a bunch of options and the last option is the output file. In this case the options are -f x11grab -s wxga -r 25 -i :0.0 -sameq and the output file is /tmp/out.mpg.

Here is what the options mean:

  • -f x11grab makes ffmpeg to set the input video format as x11grab. The X11 framebuffer has a specific format it presents data in and it makes ffmpeg to decode it correctly.
  • -s wxga makes ffmpeg to set the size of the video to wxga which is shortcut for 1366x768. This is a strange resolution to use, I'd just write -s 800x600.
  • -r 25 sets the framerate of the video to 25fps.
  • -i :0.0 sets the video input file to X11 display 0.0 at localhost.
  • -sameq preserves the quality of input stream. It's best to preserve the quality and post-process it later.

You can also specify ffmpeg to grab display from another x-server by changing the -i :0.0 to -i host:0.0.

If you're interested in ffmpeg, here are my other articles on ffmpeg that I wrote while ago:

PS. This article was so fun to write, that I decided to write several more parts. Tune in the next time for "The Next Top Ten One-Liners from CommandLineFu Explained" :)

Have fun. See ya!

PSS. Follow me on twitter for updates.

Sponsor this series!

Contact me, if you wish to sponsor any other of my existing posts or future posts!

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

Comments

asv Permalink
March 18, 2010, 11:17

Great article, picked up a few useful commands.

Alex Permalink
March 18, 2010, 11:24

Thanks for the last one! :)

A few concerns: it complains when run w/o -r, so it's pretty much required for x11grab. Also, even 500kb/s gives awful quality if you try to do a screencast, so better keep -sameq, and post-process later.

February 07, 2012, 13:04

You know, content area is one of the most overlooked about areas of a site when it comes to a style. However, it is also a very essential area as far as making a group goes.

March 18, 2010, 11:28

Alex, oh. I didn't try it. Just relied on the past experience. Okay, I will note that. That's an ok quality for a small youtube video. :) Was thinking in those terms. I will edit the post to reflect your comment.

June 26, 2011, 09:13

I will post a link to this page on my blog. I am sure my visitors will find that very useful.

morphir Permalink
March 18, 2010, 11:30

Peteris, your articles are awesome. You always provide a lucid explanation when you touch a topic. Keep it up!

March 18, 2010, 11:36

morphir, thank you!

March 18, 2010, 11:47

An amendment to #9:

yourmachine$ ssh remote-machine 'cat >> .ssh/authorized_keys; chmod -R og= .ssh' < .ssh/identity.pub

If the cat>> had to create the file, your umask would probably have made it world-readable. Hence the extra chmod.

March 18, 2010, 11:57

Tim, since it contains only public keys, it could as well be world readable, couldn't it?

Sam Permalink
March 18, 2010, 12:29

Do you guys know how to make those brace expansions work with scp??

JerBear Permalink
March 18, 2010, 12:33

I'm bookmarking and coming back to read more thoroughly. But a quick remark, command editing (#1, #5, #8) work in tcsh, or even csh just fine.

But finding out that bash shares that capability may make me switch.

Will Permalink
March 18, 2010, 12:39

ctrl + r enables you to seek through your history of commands you've runned and running it again.

I use this shortcut a lot

March 18, 2010, 12:55

Awsome, picked up some very useful tips for intermediate bash users ... top!

March 18, 2010, 13:10

I prefer using the "./" prefix when working with files or directories that start with a dash, e.g. "cd ./-".

paul Permalink
March 18, 2010, 13:57

the trouble with the historic editing commands (i.e. your #5 and #8, which as far as i remember, are old csh tricks) is that you don't get to see the command before it runs. i find (read: "have found") this to be very dangerous -- far better to use "real" commandline editing to retrieve the command you want and make the change and run it. if you're any good with your editor of choice (emacs or vi), it really won't take many more keystrokes.

paul Permalink
March 18, 2010, 14:15

re: #9 -- ssh-copy-id will also create the .ssh dir if it doesn't exist (and on a new machine it mayb not), so that's one more step it saves over other methods.

March 18, 2010, 15:41

Thanks for the commands. I haven't used a couple of those.

Yoram Stern Permalink
March 18, 2010, 16:47

The python HTTP server is just GREAT!!

Betty Permalink
April 11, 2012, 18:37

How do you exit the HTTP server output?

archuser Permalink
March 18, 2010, 17:34

The screencapture is great. There is a script called ffcast that can automate this.
http://github.com/lolilolicon/ffcast

Jeff Permalink
March 18, 2010, 18:00

Great article! Maybe to be a bit clearer, you could change these couple of sentences for tip #5

Not that this one-liner replaces just the first word in the previous command. To replace all words, add the g modifer (g for global):

to

Note that this one-liner replaces just the first occurrence of FOO in the previous command. To replace all occurrences, add the g modifier (g for global):

March 18, 2010, 18:41

One of my favorites is:

kill -9 `ps -ef | grep search_string | awk '{print $2}'` 

The above will find and kill a process that matches search string.

s@my Permalink
June 08, 2010, 17:14

Very nice, but you should eliminate the process that contain grep:
kill -9 $(ps -ef|grep search_string|grep -v grep|awk -F " " '{ print $2 }')

Pablo Permalink
June 23, 2010, 01:39

You can do it without grep too:

kill -9 $(ps -ef | awk '/search pattern/ { print $2 }')

And to avoid errors if nothing is found:

ps -ef | awk '/search pattern/ { print $2 }' | xargs -r kill -9

beli Permalink
May 03, 2012, 12:40

instead of "grep -v grep" you can also use "grep [s]earch_string" so the full line would look like:

kill -9 $(ps -ef | grep [s]earch_string | awk -F " " '{ print $2 }')

Ustun Permalink
March 18, 2010, 20:23

Guhan, any advantage over pkill?

Pablo Permalink
June 23, 2010, 01:45

pkill belongs to a package called procps, and if i remember correctly it only work on linux based systems. May be killall -s9 pattern is a more portable way to do it.

mithoviel Permalink
March 19, 2010, 00:51

Keep them coming! Thanks!

March 19, 2010, 10:41

@guhan:

pkill -f search_string

DanL Permalink
March 19, 2010, 13:26

Perhaps guhan, has to work on non-Linux servers as well.

No pkill/pgrep on AIX, HPUX, or Solaris (for that matter, no ssh-copy-id or even bash on the machines I work on either)

jra Permalink
March 19, 2010, 14:03

pkill and pgrep actually originate from solaris (solaris 7 even, suggests the linux man page for pgrep/pkill), so if your solaris does not have pgrep, your solaris is broken (or really, really old, which pretty much equals to broken I guess :)

James Permalink
March 19, 2010, 16:46

kill -9 `ps -ef |awk ' /search_string/ {print $2} '`

March 19, 2010, 16:48

As JerBear says, event designators are bash-specific only if "bash" includes csh, tcsh, zsh, and probably more.

Which is good -- more folks can use them!

hcs42 Permalink
March 19, 2010, 19:14

These commands are awesome, thanks!

March 20, 2010, 02:02

The vi save command via sudo is a gem! Thanks for sharing!

March 26, 2011, 13:50

kill -9 `ps -ef |awk ' /search_string/ {print $2} '`

Mike Permalink
March 20, 2010, 14:34

#4
cd -- - (doesn't work for me on Ubuntu 9.10 & bash version)
mike@mike-desktop:~$ bash --version
GNU bash, version 4.0.33(1)-release (x86_64-pc-linux-gnu)

cannot change to "-" directory

Any ideas why?

Thanks

March 20, 2010, 19:08

Mike, good catch! I assumed it was the default behavior of the shell commands to escape arguments after seeing --. Now I found out that it was an extension, not guaranteed to work universally. And this is one of the cases where it doesn't work.

For example, you can do things like rm -- - but you can't cd -- -.

Therefore you have to use cd ./- or cd /full/path/to/-

I am updating the article right now to fix this mistake.

sbn Permalink
March 20, 2010, 20:36

Excellent tips, and wanted to say I really enjoy your blog since discovering it recently. Great work, thanks!

March 20, 2010, 22:57

re: #7
I love mtr. It's really quite handy but I've found that if you want to share the output, you'll need to do a screenshot or use my favorite switch to the command '-r' which generates a report.

Also, you can easily restart the mtr without ^C and then repeating the command...just push the 'r' key.

--
Brie

Antti Haapala Permalink
March 21, 2010, 16:48

All these shell examples work very well in zsh too. However, with sudo !! you can see the command that is about to be run if you are using zsh... just press TAB after !! and it will expand the command from history there.

Also, another nice trick I discovered today... Many times I have had to download numbered files with wget. I used to use for loop and printf, both of which are cumbersome. In zsh, however, you can type wget http://host/directory/file{0001..1926}.ext to download 1926 files named fileNNNN.ext. Pretty neat. {n..m} kind of works in bash but bash eats the leading zeros, so instead of file0001.ext you will be trying to retrieve file1.ext.

argv Permalink
March 23, 2010, 07:17

In the *BSD /bin/sh shell I use, cd - works fine to return to the previous directory. Also, cd -- will cd to $HOME.
I think the POSIX standard requires that at least the - operand should work with cd.

As for brace expansion, if you don't use a shell that has it such as bash or zsh, and can't do it quickly with seq or a printf script, then the jot utility in *BSD base distribution can do the job. It appears specifically designed for these type of tasks.

commandlinefu has a few good one-liners, but it is very Bash-centric. I'd like to see a collection of fu that's more portability-focused.

/bin/sh in vi mode is what I use as an interactive shell. I began this as an experiment, and to my surprise I never went back to another shell. I have put sh to heavy interactive use and it is more than enough, for me. The Open Group's Base Specification gives a decent list of all that vi mode can do.

When you use /bin/sh as your interactive shell, life becomes a little bit simpler, and, believe it or not, easier.

March 24, 2010, 09:25

Peteris Krumins: no. A lot of the time sshd_config species strict permissions, which includes the authorized_keys file *not* being world-readable.

April 05, 2010, 13:14

I accidentally left this comment under the wrong article :). Posting it here too, (sorry Peteris, I'd delete the other one if I could).

Reading this post, I realized you could use the set expansion stuff for a rudimentary calculator. I'm not arguing this is a _good_ tool, it just works, for natural numbers.

For instance, 3*5
$ echo {1,2,3}{1,2,3,4,5} | wc -w
15

Also, here's a way you can print every number from 1 to 99 (I won't print the output, you can try it on your own).

$ echo {,1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9}

Daniel Permalink
April 13, 2010, 14:58

Some good tips I didn't know.

Daniel.

Jason Permalink
April 23, 2010, 20:22

#3 is great -- I am always trying to edit files in /etc and forgetting to become root or use sudo.

But, based on your explanation, I'm not sure why the following doesn't work:

:w !sudo cat > %

It seems that should also be able to write to the file, but I get a permission error when I try it

:w !sudo cat > %
/bin/bash: /tmp/root.txt: Permission denied

shell returned 1

Any ideas?

Pablo Permalink
June 23, 2010, 02:00

> is interpreted by your shell (with your rights).

April 30, 2010, 07:49

Concerning tip #7: strictly speaking, mtr is not fully replaces traceroute because mtr use ICMP protocol for sending its probes while traceroute use UDP. This can be important in some firewalled environments.

galaxywatcher Permalink
November 12, 2011, 19:05

mtr has the -u switch which allows you to toggle UDP and ICMP ECHO datagrams.

Wow, great stuff! I'm totally going to use these when screwing around in Ubuntu

Fajry Permalink
May 31, 2010, 08:50

I really liked your article, containing much information that is useful to others.
thanks for everything

Fajry Permalink
May 31, 2010, 08:52

base distribution can do the job. It appears specifically designed for these type of tasks.

commandlinefu has a few good one-liners, but it is very Bash-centric. I'd like to see a collection of fu that's more portability-focused.

Fajry Permalink
May 31, 2010, 08:53

I love mtr. It's really quite handy but I've found that if you want to share the output, you'll need to do a screenshot or use my favorite switch to the command '-r' which generates a report.

June 04, 2010, 07:09

I watched artkel you made and I conclude that your article was great that I get a lot from here. and your article is helpful in accelerating my job. thank you very much for everything

infogrok Permalink
June 22, 2010, 10:09

thank you for the explanation.
Now I really how to use linux.

August 02, 2010, 14:40

Perhaps guhan, has to work on non-Linux servers as well.

No pkill/pgrep on AIX, HPUX, or Solaris (for that matter, no ssh-copy-id or even bash on the machines I work on either)

August 28, 2010, 12:24

thanks ..

September 02, 2010, 15:20

Thanks for #2. :)

vishnu Permalink
September 05, 2010, 07:48

Bur it says fsta:command not found

September 08, 2010, 10:41

ssh-copy-id is not a standard command but is part of Portable OpenSSH. You can download the whole thing from them and just copy that script to your scripts directory though. That worked for in using OS X.

October 21, 2010, 06:28

#3 is great -- I am always trying to edit files in /etc and forgetting to become root or use sudo.

But, based on your explanation, I'm not sure why the following doesn't work:

:w !sudo cat > %

It seems that should also be able to write to the file, but I get a permission error when I try it

:w !sudo cat > %
/bin/bash: /tmp/root.txt: Permission denied

shell returned 1

statistici pariuri Permalink
October 30, 2010, 15:42

I am really enjoying reading your well written articles. I think you spend numerous effort and time updating your blog. I have bookmarked it and I am taking a look ahead to reading new articles. Please keep up the good articles!

November 01, 2010, 01:55

If only I would know sudo !! earlier:S

November 05, 2010, 13:40

Thanks for this. Funnily enough the two that were unfamiliar to me were the simple commands mtr and ssh-copy-id. Shows how you can be familiar with lots of "clever stuff" but have no clue that these great, forehead-slappingly obvious tools even exist.
I just had to go show them off to someone else in the office!

WillPalm Permalink
August 22, 2011, 09:47

Fantastic information and great story. Very happy to read about it!

ranjan Permalink
November 13, 2011, 06:59

This blog has definitely changed my perspective on this subject. There's no way I would've thought about it this way if I hadn't come across your blog. All I was doing was cruising the web and I found your blog and all of a sudden my views have changed. Good on you, man!

pandeeswaran Permalink
February 05, 2012, 15:51

But , the set expansion technique always expecting more than 1 value.
e.g:
echo {1}{2} returns {1}{2} where we expect 12.
It can be simply achieved by echo 12.But Set expansion always expect more than 1 value.

nawaz Permalink
April 10, 2012, 17:06

very good article. i am really impressed! ! !

johndavid Permalink
May 17, 2012, 12:49

Once again a very helpful post. I have been reading many of the advice and posts on this website and find that they are very helpful and interesting as well as all comments. Thanks for the great advice so far.

Thorsen Permalink
June 19, 2012, 07:30

@bell
I'm puzzled how your:

kill -9 $(ps -ef | grep [s]earch_string | awk -F " " '{ print $2 }')

works. I mean the "[s]earch_string" bit.

I can see that it results in the grep process being dropped from the result, but just can't see why this is :)

Could you explain? Cheers!

Fred Permalink
October 02, 2012, 21:37

These are already explained on commandlinefu. Why plagiarise?

Achar Locais Permalink
October 26, 2012, 23:12

These are already explained on commandlinefu

Joe Walsh Permalink
February 08, 2014, 14:52

Hi Peteris, thanks for your great website. Only learned of it in the past few weeks.

Re #5: As a previous poster pointed out, this is an old csh trick that I've known about for over two decades. And the third carat (^) is not needed, at least in csh. So which part of that trick is bash-specific? The globalized replacement feature, perhaps?

May 21, 2014, 14:54

Great! Thanks!

Leave a new comment

(why do I need your e-mail?)

(Your twitter name, if you have one. (I'm @pkrumins, btw.))

Type the word "browser_195": (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.

Advertisements