I Know Shell Fu

If you're a developer you will likely be spending a lot of time in the command line. Here's a few tips and tricks I've learned over the years.

Basic commands you should know

If you want to list all of the directories and files in the current directory you would use the ls command:

$ ls /var/log
=> system.log
=> shutdown_monitor.log
=> ...

In programming we often have a lot of configuration files that are hidden. These are referred to as dotfiles because they start with a dot e.g. .file You can modify the list command by using the -a (all) flag e.g. ls -a which will show all files including your hidden config files:

$ ls -a
=> .git/
=> .ruby-version
=> .oozou

Now suppose you want to delete a directory, but it has files inside of it. Instead of reaching for finder,  you can use the rm command, but that will only delete single files or empty directories. If you need to delete the entire directory you can use the -r (recursive) flag e.g. rm -r which will delete all of the files in the directory as well as the directory itself. You can also use wildcard expressions to match specific files in a directory too. Perhaps you want to delete all the text files:
$ rm -r dirname/*.txt
=> Deleted 10 files matching dirname/*.txt

Occasionally you'll find that some of the files are write-protected which will prompt you to confirm you really want to delete the file. If you are recursively deleting write-protected files it can get really tedious inputting y to confirm their deletion over-and-over. To get around this we can supply the -f  (force) flag to tell the remove command to go ahead without asking:

$ rm -rf dirname/write-protected.*.txt
=> Deleted 10 write protected files without bothering you for permission

Some shell shortcuts

Ever typed a command and forgot sudo? Did you know you can use !! to replay the last command. This is really useful for sudoing commands that failed:

$ rm -rf /important-directory
=> permission denied
$ sudo !!
=> I'm sorry, I didn't realize it was you, go right ahead

You can view a history of all your commands by typing history into the command line:
$ history
=> 1 ls /var/log
=> 2 ls -a
=> 3 rm -r dirname/*.txt
=> 4 rm -rf dirname/write-protected.*.txt
=> 5 rm -rf /important-directory
=> 6 sudo rm -rf /important-directory

You can also use !-n (where n is a number) to copy the command you used n commands ago:

$ pwd
=> /Users/joe/foo
$ cd ~
$ !-2
=> /Users/joe

You can also use !substring to use the last command that started with that string:

$ ffmpeg -i video.flv video.mpeg
=> video.flv not found
$ ls
=> thumbnail.png
$ cd ..
$ ls
=> thumbnails/ videos/
$ cd videos
$ !ffmpeg
=> converting video.flv to video.mpeg

Then we can do some more adventurous things like replacing text in the previous command:

$ rm -r dir/foo/*.rb
=> removed all ruby files in dir/foo/
$ ^foo^bar
=> removed all ruby files in dir/bar/

Some Rails tricks I use often

Adding a file to .gitignore without opening the editor is as simple as shoveling it in. (NOTE be careful not to use > or you'll overwrite the entire file - we want >> to append the text):

$ "public/assets" >> .gitignore
=> appended "public/assets" to .gitignore

Need to find a route in your Rails application? You know some substring in the route but not sure the exact route. You can grep for it by piping the output from bin/rails routes to grep and then use the substring to narrow down the routes:

$ bin/rails routes | grep auth
=>      oauth_token POST  /api/v1/oauth/token(.:format)        doorkeeper/tokens#create
=>     oauth_revoke POST  /api/v1/oauth/revoke(.:format)       doorkeeper/tokens#revoke
=> oauth_introspect POST  /api/v1/oauth/introspect(.:format)   doorkeeper/tokens#introspect

Store the output of a script in a file by redirecting the output like we did before when adding public/assets to our .gitignore. However, this time we'll overwrite the whole file:

$ ruby ./scripts/1_million_dollars.rb > ./log/1_million_dollars.txt
=> No output here
$ cat ./log/did_i_get_1_million_dollars.txt
=> Don't you think we should maybe ask for more than a million dollars?
=> A million dollars isn't exactly a lot of money these days.
=> Virtucon alone makes over 9 billion dollars a year!

Or if you want to log the STDERR output to its own file:

$ ruby ./scripts/raise_allota_errors.rb 2> ./log/allota_errors.text
=> STDOUT output here
$ cat ./log/allota_errors.text
=> STDERR output here

You can use less to view a file. While less is running you use /substring to search the file, or up and down arrows to navigate around. Less is very similar to more but is faster because it doesn't load the full file into memory.

$ less ./log/production.log

You can also use tail to watch the "tail" of a file in real-time, this is useful for watching logs while they are being written. Make sure to use the "follow" flag to watch it in real-time or you'll only see the last few lines of the file (If you're on a machine that is rotating the logs, you can use -F to keep watching the file even after it has been recreated during rotation):

$ tail -f ./log/production.log
=> ...
=> ...
=> ...

Some other useful commands

Ever needed to count how many lines are in a file? You can use wc (word-count) and pass the -l (lines) flag to count the lines:

$ wc -l ./users_dump.csv
=> 1337

You can also use this to count how many lines in the output of a command e.g. find out how many files are in a directory with:

$ ls ./node_modules | wc -l
=> 9284728927498392847393

Ever needed to write a long command and feel frustrated with the editing experience in your terminal? Open your EDITOR and edit the command instead by pressing ctrl-x ctrl-e:

$ some-long command ops I spelled that thing wrong ctrl-x ctrl-e
#> *opens your EDITOR*
#< save and close your editor to re-enter shell
$ some-long command I spelled correctly now

For the above to work you'll need to export EDITOR=your_editor in your .bashrc or .zshrc. Some editors will need to use wait mode to return to the terminal after editing. If you're a MacVim user you'll need to pass a command to run after VimLeave like I have here:

export EDITOR='mvim -f -c "au VimLeave * !open -a iTerm"'

I know shell fu

Show me!

The terminal is incredible! It's where we began but continues to be one of the most powerful tools in my day-to-day activities. There's loads of stuff I haven't even mentioned in this article. I recommend reading more if any of this interests you. The shell has been around as long as modern computers so there's a huge amount of information available. If you know any shell tricks I'd love to hear them.
Like 2 likes
Joe Woodward
I'm Joe Woodward, a Ruby on Rails fanatic working with OOZOU in Bangkok, Thailand. I love Web Development, Software Design, Hardware Hacking.

Join the conversation

This will be shown public
All comments are moderated

Get our stories delivered

From us to your inbox weekly.