---
title: "I Know Shell Fu"
description: "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."
canonical: https://oozou.com/blog/i-know-shell-fu-81
locale: en
date: 2020-08-02
author: "OOZOU Team"
---

# 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.

Published: 2020-08-02 · Author: OOZOU Team (Engineering) · 6 min read

# 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:

```shell
$ 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:

```shell
$ 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:

```
$ <em>rm -r dirname/*.txt
=> Deleted 10 files matching dirname/*.txt
language-shell</em>
```

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:

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

# 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:

```shell
$ 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 <em>rm -r dirname/*.txt</em>
=> 4 <em>rm -rf dirname/write-protected.*.txt
</em>=> 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:

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

You can also use **_!substring_** to use the last command that started with that string:

```shell
$ 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:

```shell
$ 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):

```shell
$ "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:

```shell
$ 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:

```shell
$ 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:

```shell
$ 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.

```shell
$ 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):

```shell
$ 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:

```shell
$ 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:

```shell
$ 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:_**

```shell
$ 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_:
_

```shell
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.

---

Contact OOZOU: hello@oozou.com · +66 2544 0000 · https://oozou.com/contact

More for AI agents: https://oozou.com/llms.txt · every page has a markdown twin at its URL plus ".md"
