I would like to open a discussion that would accumulate your Linux command line (CLI) best practices and tips.

I've searched for such a discussion to share the below comment but haven't found one, hence this post.

I hope we all could learn from this.

You are welcome to share your Bash tips, grep, sed, AWK, /proc and all other related Linux/Unix system administration, shell programming best practices for the benefit of us all.

  • Why Linux only? Many tips may be useful on almost all flavours of Unix. – mouviciel Mar 2 '09 at 19:49
  • This question might be a tad broad - there is enough to Linux/Unix command line best practices to fill entire books... – Jonik Mar 2 '09 at 19:50
  • I agree that unix tips that can be applied to linux also are very much welcome, I've written linux because A. This is what we work with here. B. because it's gaining a more wider public exposure then unix in recent years. – Maxim Veksler Mar 2 '09 at 19:57
  • I think it's a great idea to comiple a list of the most useful and interesting commands that people use. – Shane Mar 2 '09 at 19:57
  • Are any of these relevant? refspecs.freestandards.org – S.Lott Mar 2 '09 at 19:58

85 Answers 85

1 2

I'm moving towards never typing "rm" at a command prompt. Instead, I type "ls", and if I like the list of removed files I edit the command (easily possible with bash and ksh).

Edit to add something from the comments: "rm -i" will prompt for each deletion, which accomplishes the same purpose. Thanks!

  • Please explain, I can't understand what you mean? – Maxim Veksler Mar 2 '09 at 20:24
  • Typing something like "rm *.txt.bak" is a little error-prone; if you type "rm * .txt.bak" by mistake you're in trouble. Therefore, I'll type "ls *.txt.bak" to see if I'm selecting the files I think I am; then I use bash shell command-line editing to substitute "rm" for "ls". – David Thornley Mar 2 '09 at 20:44
  • How do you do the command-line substitution? There must be a faster way than hitting up, home, delete, delete, r, m. – dotjoe Mar 2 '09 at 22:23
  • I don't know of a faster way. I type faster and better than most people I hang out with, and it seems that I am willing to type more than they are. I think that in csh and tcsh ^ls^rm or something like that worked. – David Thornley Mar 3 '09 at 14:36
  • Using 'rm -i filename' works for me. It will prompt me to remove the file. I actually aliased rm to 'rm -i'. – gpojd Mar 3 '09 at 16:54

You can do some simple networking with bash (credit to this page and man bash):

cat < /dev/tcp/time.nist.gov/13

Yes, writing to and simultaneous reading-writing is also possible.

  • Alas, Bash from Ubuntu doesn't seem to have this feature enabled :-( Wait for newer version. – kyku Mar 5 '09 at 8:11
  • I beleive this is because Bash from Ubuntu isn't compiled with the option to enable networking. Don't know what reasons keep them from enabling it. But I couldn't find a proper use for this feature anyway. :) – Eugene Morozov Mar 5 '09 at 11:23

Above all, understand anything you see in the internetz before trying it out in your dev box.


Every now and then neither of:

find . -exec ...
find . -print0 | xargs ...
for i in *.png; do ... done

works for processing a list of files, since one needs the combined power of find, NULL separated filenames as well as plain shell loops. The solution is this little bit of bash code:

find . -print0 | while read -r -d $'\0' filename; do echo $filename; done

That allows one to process NULL separated files in a normal loop.

  • What is the problem with the third? for i in *.png; do...done? – Juliano Mar 29 '09 at 3:16
  • GNU Parallel gnu.org/software/parallel seems to be better suited for this, as it can run jobs in parallel. The while-read loop can be rewitten as: find . -print0 | parallel echo Watch the intro video: youtube.com/watch?v=OpaiGYxkSuQ – Ole Tange Jun 22 '10 at 12:09

Shell-fu is a place for storing, moderating and propagating command line tips and tricks. A bit like StackOverflow, but solely for shell. You'll find plenty of answers to this question there.


Another trick:

When I want to make a bash alias, I just make a bash script on my user bin folder.

For instance, instead of adding the following line to my .bashrc,

alias make-symlink='ln -s -iv'

I'd make the following script and save it as ~/bin/make-symlink

ln -s -iv "$@"

, once the script is made executable (chmod +x), it's like I have a new alias.

Now make-symlink can be used in xargs. Also when you use a different shell (ZSH, FISH, IPYTHON, ...), make-symlink is there too.

If you use emacs, you might want to add the following to your emacs init file.

;; Make scripts executable on save                                              
(add-hook 'after-save-hook 'executable-make-buffer-file-executable-if-script-p)

The 'tee' command is really useful for when you are outputting to a file and want to see the progress at the same time. This is especially helpful for when you are logging output to a file and need to watch it as it progresses.

Instead of doing something like:

./program > file &
tail -f file

You can use the tee command on one line:

./program | tee file

I like to keep track of everything I do. One command that I learned in college was 'script'. This takes any output on your terminal and logs it to a file. What I didn't learn in college is how to make every terminal a script. Now I have this in my .login file:

exec script ~/.typescript/`date +%Y%m%d%H%M%S`.$$

Make sure that ~/.typescript/ exists before you add that to the end of your .login file. :)

  • For smaller directory trees with documentation to browse

    find .
  • To empty a file from shell

    > file.txt
  • To return to my home directory


IMHO, *nix most important command ever is... man :)

Almost everything one needs to know can be found with man and using man prevents us from interrupting our co-workers. And dealing with interruptions is one of our biggest concerns...


I found Git version control to be:

  • Snappy
  • A pleasure to use
  • Useful for a projects of almost any size (100K to 100GB; 1 to 100k files)

Here is how I do it:

# Create new repository
# (for now, it will live in .git/ - a single directory)
git init

# Commit all I got so far
git add .
git commit

# Add new or modified files manually
git add *.c
git status
git commit

# Add all modified files
git status
git commit -a

# Redo last commit
git commit -a --amend

# View log
git log

# Reset everything (files and git history) back to 
# what it was at 96223554b3e3b787270b1f216c19ae38e6f83ca5
git branch this-was-a-mistake
git reset --hard 9622

# Everything is back in time
git log

Easy sums/averages/grouping with awk:

cat tests
ABC 50
DEF 70
XYZ 20
DEF 100
MNP 60
ABC 30

cat tests | awk '{sums[$1] += $2; tot += $2; qty++}\
   END { for (i in sums) 
     printf("%s %s\n", i, sums[i]); 
     printf("Total: %d\nAverage: %0.2f\n", tot, tot/qty)} ' 
MNP 60
ABC 80
XYZ 20
DEF 170
Total: 330
Average: 55.00

I dont these commands are in the above list...!!!

  1. find . -name .svn -type d |xargs rm -rf

    Remove all .svn folders

  2. bash -x script.sh

    print line and execute it in BASH

  3. Ctrl + [

    the same as [Esc] in vim

  4. shopt -s autocd

    Automaticly cd into directory

  5. df -i

    View the current number of free/used inodes in a file system

  6. sudo !!

    Run the last command as root

  7. python -m SimpleHTTPServer

    Serve current directory tree at http://$HOSTNAME:8000/

  8. netstat -tlnp

    Lists all listening ports together with the PID of the associated process

  9. Below are some ways to number input.txt:

    cat -n

    $ cat -n input.txt 1 123 2 3 456 4 5 789 6 7 8 abc 9 10 def 11 12 ghi


Output Redirection. When you're running something apend

> ~log.txt

to capture the output for later. Append

>& error_and_log.txt

for errors as well.


pushd and popd to temporarily switch to different directories.


pushd ~/tmp

will move you to that directory, but push your current location to a stack (so it can be nested).



to return to the previous location.


Turn on inline mode for tab completion for Bash:



To open remote X application on you local machine do:

ssh -X remoteuser@removeHost

This allows quickly viewing graphical applications running on remote host yet using your machine as the graphical server.


Copy selected text:


...and paste it:

  • You can paste the last selected text by clicking on the middle mouse button. – Maxim Veksler May 4 '09 at 15:54

In bash to close file descriptors: FD>&-

Close stderr:

$ function echostderr() { echo $1 >&2; }
$ echostderr "now you see me"
now you see me
$ echostderr "now you don't" 2>&-

Or inside a script:

$ function echostderr() { exec 2>&-; echo $1 >&2; }
$ echostderr "now you don't"

ALWAYS start any command or pipeline with # (comment) and remove it when you finish writing the command. Gives you a 2nd chance at spotting rm -rf / like things.


Here are Collection of Linux, UNIX User Management Commands http://www.linuxconfig.net/2009/11/16/linux-unix-user-management-commands.html


Meta+. in bash for cycling through the last argument from previous commands. Great for running tail and grep in various combinations.

  • Note for OS X users: In Terminal, meta needs to be enabled under the Keyboard settings before this will work. – Jeremy M Jul 14 '10 at 17:27

CTRL+] x to forward search for a character "x", and Meta, CTRL+] x for backward search. On most systems, Meta can be ESC or ALT. For ESC, you press ESC then release, then combine CTRL and ], and then press the character to search for. For ALT, press down CTRL + ALT + ] at the same time, then the target character.

I find it's useful when editing history command.

For very long and very complicated command. I use fc to open vi(probably actually vim on linux) to edit the command.

  • 1
    For long command editng, in bash at least, you can 'set -o vi' to enable vi-style keybindings directly in your shell. – pboin Oct 25 '10 at 14:49

To rename a multiple files in a similar fashion I found the following script very useful and robust over the years.

It just puts the output of ls into your favorite text editor. You just modify the text, save, close. The files are renamed accordingly.

It's especially great when you combine this with Vi column editing (Ctrl-v, select a block, I to insert before or A to insert after, type text, Esc).


RM = '/bin/rm'
MV = '/bin/mv'

from = Dir.entries('.').sort; from.delete('.'); from.delete('..')

from.delete_if {|i| i =~ /^\./} # Hidden files

tmp = "/tmp/renamer.#{Time.now.to_i}.#{(rand * 1000).to_i}"

File.open(tmp, 'w') do |f|
  from.each {|i| f.puts i}

ENV['EDITOR'] = 'vi' if ENV['EDITOR'].nil?
system("#{ENV['EDITOR']} #{tmp}")

to = File.open(tmp) {|f| f.readlines.collect{|l| l.chomp}}
`#{RM} #{tmp}`

if to.size != from.size
  STDERR.puts "renamer: ERROR: number of lines changed"

from.each_with_index do |f, i|
  puts `#{MV} -v --interactive "#{f}" "#{to[i]}"` unless f == to[i]

I call this script renamer.


To copy part of a file system to a new hard disk one can use

mkfs.ext4 /dev/sdb
mkdir /mnt/newhd
mount /dev/sdb /mnt/newhd/
rsync -av --hard-links --acls --one-file-system --xattrs /home/maxim/ /mnt/newhd/
echo '/dev/sdb /home/maxim ext4 defaults,user_xattr,noatime 0 1' >> /etc/fstab
1 2

Not the answer you're looking for? Browse other questions tagged or ask your own question.