Follow me on Twitter for my latest adventures!

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:
- Working Efficiently in Bash (Part I).
- Working Efficiently in Bash (Part II).
- The Definitive Guide to Bash Command Line History.
- A fun article on Set Operations in the Shell.
- Another fun article on Solving Google Treasure Hunt in the Shell.
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 x11grabmakes 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 wxgamakes 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 25sets the framerate of the video to 25fps.-i :0.0sets the video input file to X11 display 0.0 at localhost.-sameqpreserves 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:
- How to Extract Audio Tracks from YouTube Videos
- Converting YouTube Flash Videos to a Better Format with ffmpeg
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!


Facebook
Plurk
more
GitHub
LinkedIn
FriendFeed
Google Plus
Amazon wish list
Comments
Great article, picked up a few useful commands.
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.
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.
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.
I will post a link to this page on my blog. I am sure my visitors will find that very useful.
Peter, your articles are awesome. You always provide a lucid explanation when you touch a topic. Keep it up!
morphir, thank you!
An amendment to #9:
If the cat>> had to create the file, your umask would probably have made it world-readable. Hence the extra chmod.
Tim, since it contains only public keys, it could as well be world readable, couldn't it?
Do you guys know how to make those brace expansions work with scp??
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.
ctrl + r enables you to seek through your history of commands you've runned and running it again.
I use this shortcut a lot
Awsome, picked up some very useful tips for intermediate bash users ... top!
I prefer using the "./" prefix when working with files or directories that start with a dash, e.g. "cd ./-".
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.
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.
Thanks for the commands. I haven't used a couple of those.
The python HTTP server is just GREAT!!
How do you exit the HTTP server output?
The screencapture is great. There is a script called ffcast that can automate this.
http://github.com/lolilolicon/ffcast
Great article! Maybe to be a bit clearer, you could change these couple of sentences for tip #5
to
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.
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 }')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
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 }')
Guhan, any advantage over pkill?
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.
Keep them coming! Thanks!
@guhan:
pkill -f search_string
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)
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 :)
kill -9 `ps -ef |awk ' /search_string/ {print $2} '`
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!
These commands are awesome, thanks!
The vi save command via sudo is a gem! Thanks for sharing!
kill -9 `ps -ef |awk ' /search_string/ {print $2} '`
#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
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.
Excellent tips, and wanted to say I really enjoy your blog since discovering it recently. Great work, thanks!
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
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.
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.
Peter Krumins: no. A lot of the time sshd_config species strict permissions, which includes the authorized_keys file *not* being world-readable.
I accidentally left this comment under the wrong article :). Posting it here too, (sorry Peter, 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}
Some good tips I didn't know.
Daniel.
#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?
> is interpreted by your shell (with your rights).
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.
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
I really liked your article, containing much information that is useful to others.
thanks for everything
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.
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.
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
thank you for the explanation.
Now I really how to use linux.
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)
thanks ..
Thanks for #2. :)
Bur it says fsta:command not found
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.
#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
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!
If only I would know sudo !! earlier:S
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!
Fantastic information and great story. Very happy to read about it!
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!
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.
very good article. i am really impressed! ! !
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.
@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!
These are already explained on commandlinefu. Why plagiarise?
These are already explained on commandlinefu
Hi Peter, 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?
Great! Thanks!
I have been reading many posts on this website and find that they are very helpful and interesting as well as all comments. Thanks for sharing.
Peter, your articles are awesome.
Leave a new comment