There are occasions when we want to run a series of commands repeatedly in the Linux shell and, as usual with Linux, there’s more than one way to do that.
Command Recall
The example we’re probably all familiar with is the keyboard up arrow. Each press of that key will step back through the command history (unless we’ve hidden it) and let us re-execute an earlier command.
There are a number of tricks to improve this functionality, and one of my favourites is to include this in the /etc/inputrc
file:
# map "page up" and "page down" to search the history "\e[5~": history-search-backward "\e[6~": history-search-forward
Now, if you want to ssh
to a system you have ssh
‘d to before, type ssh
and press the Page Up key. Each time you press it, the previous command that starts ssh
will be shown.
Others prefer to type ^R
followed by any part of a previous command, which will display the most recent command that matches whatever was typed after the ^R
. You can step back through earlier matches by repeating ^R
.
Command Recall Loops
In a somewhat contrived example, let’s suppose you are making some tweaks to an Apache site configuration file. Your most recent command line history is:
# vim /etc/apache2/sites-enabled/www.example.com.conf # apache2ctl -t # apache2ctl graceful
But all is not well, and you need to make a further edit. Press the up arrow to recall the vim
line, then instead of pressing return, type ^O
. The editor will be loaded and you can make your changes. When you return to the command line, the next command – apachectl -t
– will already be loaded. Again, press ^O
, which will run the command and place the last command above on the command line. If you again press ^O
, bash
will loop back to the vim
command.
I will confess I don’t use this very much, but it seemed too interesting to ignore!
For Loops
One construction I do use a lot is for
loops. Let’s suppose you have some WAVE audio files that you want to convert to mp3 files. We can achieve that with:
for a in *.wav;do ffmpeg -i $a ${a%.wav}.mp3;done
This will:
- find each
*.wav
file in the current directory and assign each in turn to thebash
variablea
- run
ffmpeg
with the.wav
file as the input
The output filename is derived from ${a%.wav}.mp3
. This:
- takes the variable
a
and removes from the end the shortest pattern that matches.wav
, so formyfile.wav
,${a%.wav}
will result inmyfile
- that shortened name has
.mp3
appended
A useful tip when building for
loops is to precede commands with echo
so that you can see what will be done:
$ for a in *.wav;do echo ffmpeg -i $a ${a%.wav}.mp3;done ffmpeg -i soundfile1.wav soundfile1.mp3 ffmpeg -i soundfile2.wav soundfile2.mp3 ffmpeg -i soundfile3.wav soundfile3.mp3 ffmpeg -i soundfile4.wav soundfile4.mp3 ffmpeg -i soundfile5.wav soundfile5.mp3 ffmpeg -i test.wav.wav test.wav.mp3
Once you’re happy with the result, you can command-recall, remove the echo
and run.
Could This Linux Tip be Improved?
Let us know in the comments below.
I started using fish as my shell on my main workstation which works like tip number 1 by default – plus more …