DEV Community

Cover image for 5 Handy Bash Tricks in 2 Minutes

5 Handy Bash Tricks in 2 Minutes

Jacob Herrington (he/him) on August 12, 2019

This article should only take you a couple of minutes to read, and you'll walk away with a handful of the bash tricks I constantly use. 1...
Collapse
 
learnbyexample profile image
Sundeep

Nice list.

Please please please please please use double quotes around variables, unless you explicitly desire the shell to interpret the variable value. A simple example to illustrate the issue:

$ mkdir test_shell_behavior && cd $_
$ touch 'foo' 'xyz 123'
$ for file in * ; do cp $file $file.bak; done
cp: target '123.bak' is not a directory
$ ls
foo  foo.bak  xyz 123

$ rm foo.bak
$ for file in * ; do cp "$file" "$file".bak; done
$ ls
foo  foo.bak  xyz 123  xyz 123.bak
Enter fullscreen mode Exit fullscreen mode

See this Q&A on unix.stackexchange and wooledge: Quotes for more details.


You can also use man -k instead of apropos


For testing shell globs, you can use echo

$ echo cp /some/path/to/file.txt{,.bak}
cp /some/path/to/file.txt /some/path/to/file.txt.bak
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jacobherrington profile image
Jacob Herrington (he/him)

Cool! Thanks for the feedback, that is good advice. 🤠

Collapse
 
vasilvestre profile image
Valentin Silvestre

I definitly recommand ${variable}, much better to look and feel more like you can't be wrong about it :)

Collapse
 
zeddotes profile image
zeddotes • Edited

To piggyback on the last one there, if you history:

$ history

the output should be a numerical list of commands you've entered in the past, like so:

10016  git status
10017  git commit
10018  git push origin master

If I wanted to run git status again (ie, repeat command 10016 from history), I could do something like this:

$ !10016

Collapse
 
jacobherrington profile image
Jacob Herrington (he/him)

That's a great trick! Thanks!

Collapse
 
zeddotes profile image
zeddotes • Edited

You could also pipe history too! Which could allow you to grep (and more) within your history:

$ history | grep "status"

10005  git status
10008  git status
10011  git status
10014  git status
10016  git status

:)

Thread Thread
 
ianturton profile image
Ian Turton

ctrl-r status will walk back up your history looking at each time the command line contained status - just hit return when you find the command you need.

Collapse
 
ssimontis profile image
Scott Simontis

My favorite piece of knowledge:

If you choose to make an alias for sudo, you should ensure there is a space at the end, or else the shell will not expand any more aliases within the command.

So if I wanted to use please instead of sudo, I would need to do:

alias please="sudo "

I am always tempted to put spaces around the equals sign, probably my #1 scripting mistake.

Collapse
 
alsargent profile image
Al Sargent

Some of my regular bash commands:

A simple way to monitor network connectivity and latency -- ping a highly-available domain like google.com:

ping google.com | while read pong; do echo "$(date): $pong"; done

I also like to use alias to shorten frequently-used commands and parameters. Here are some examples from my .bashrc:

alias l="ls -aF"
alias ll="ls -laF"
alias c="cd"
alias m="more"
alias e="env | sort"
alias h='history'
alias p="pwd"
alias g="go run"
alias cd..='cd ..'
alias c..='cd ..'
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias .....='cd ../../../..'
alias ......='cd ../../../../..'

Print each PATH entry on a separate line:

alias path='echo -e ${PATH//:/\n}'

Open the current directory in macOS Finder:

alias o="open ."

Collapse
 
fernandomaia profile image
Fernando Maia • Edited

Really cool! Just learned about $_.

If anyone wants, I just created a function to perform the first item on the list in a single command.

function mdcd {
    command mkdir $1 && cd $1
}

This should work with bash and zsh, and should be added to their config files(.bashrc and .zshrc, respectively).

Collapse
 
nickhuanca profile image
Nick Huanca

Just a heads up, globbing in your "backup every file" example will miss all dotfiles. You could do a dual expansion in this case for i in * .*; do echo "${i}"; done but this also includes . and .. in this glob. YMMV depending on what you're doing with the list.

Collapse
 
jacobherrington profile image
Jacob Herrington (he/him)

Thanks for the heads up!

Collapse
 
simbo1905 profile image
Simon Massey • Edited

ctrl+r to search back through your command history to rerun a command is something that I find surprises people

vim folks will also love setting vi as the keyboard shortcuts to edit lines in Bash with set -o v within their profile. you can then up arrow (or ctrl+r) to find a previous command, hit Esc to exit insert mode to activate vi shortcuts then to edit the line. For example command 0 (zero) takes you to the beginning of the line, w jumps over words, r will let you overwrite, a to start appending, d3w will delete three words. Using vi shortcuts can save a serious amount of typing.

Folks on windows who like bash and who use visual studio code might like my post on setting Git Bash to be the built in terminal within VS Code.

Collapse
 
peterwitham profile image
Peter Witham

Thanks for these, sometimes it's the little things that you forget and need to read to be reminded. The create folder and change to it is priceless, I turned my one into a function so I can use it generically.

Collapse
 
dbazuin profile image
Dirk Bazuin

When using mkdir to create /foo/bar adding -p makes sure that /foo is also created if is does not exist yet.

Collapse
 
kristian profile image
Kristian

In number 3, is moving every file in a folder just an example to illustrate using a for loop? Or is there a reason why you wouldn't do it like this:

mv * ../elsewhere/
Collapse
 
jacobherrington profile image
Jacob Herrington (he/him)

Haha, no.

I updated it to a more legitimate use. I didn't actually want to use the example of making backups twice in a row, but the moving example didn't make sense. 🙃

Collapse
 
andyyaldoo profile image
Andy Aldo Dharmawan • Edited

For point number 2, does it expand to

cp /some/path/to/file.txt /some/path/to/file.bak

?

If so, shouldn't it be

cp /some/path/to/file{.txt,.bak}

?

Collapse
 
learnbyexample profile image
Sundeep

I guess the author wished to only add a suffix, not change the extension itself.

$ echo /some/path/to/file.txt{,.bak}
/some/path/to/file.txt /some/path/to/file.txt.bak
Collapse
 
jacobherrington profile image
Jacob Herrington (he/him)

Either one works here. I usually leave the original file extension on these!

Collapse
 
brianjenkins94 profile image
Brian Jenkins • Edited

If you're writing shell scripts, and you're not using ShellCheck, you're missing out.

Collapse
 
jacobherrington profile image
Jacob Herrington (he/him)

Good advice!