embed multiple scripts in a shell script

Sometimes, we need to run awk or sed or other scripts inside a shell script. Although scripts usually can be passed to interpreters by argument, this is not easy for any serious scripts that consists of more than a few lines. Yes, you can put scripts in separate script files, but sometimes I don’t want a dozen small files to do a small task.

Here shows some great examples of embedding scripts using here document.

There is another way to do it by extracting the embedded script from the shell script using sed, like this.

Now we can write:

# any shell script
awk -f <(sed -n '/^__AWKCODE1__/,/^__AWKCODE1__/p' $0)
# any shell script
exit 0
# end the script here
__AWKCODE1__
# any awk code
__AWKCODE1__
# you can have more scripts here

This script will now extract part of itself as an awk script and feed to awk. Here <() is used to get the output of a command and assign a named pipe to it and place the name of the pipe in the command argument of awk -f. The command inside parenthesis is to tell sed to print a particular range of lines bounded by these patterns and suppress output of every other lines (by -n).

In this way, you can put multiple scripts inside the shell script. And since I use emacs, I can change the major mode to awk-mode to edit awk scripts and back to shell-mode afterwards to automatically format the indention and also have syntax highlighting.

Sponsored Post Learn from the experts: Create a successful blog with our brand new courseThe WordPress.com Blog

WordPress.com is excited to announce our newest offering: a course just for beginning bloggers where you’ll learn everything you need to know about blogging from the most trusted experts in the industry. We have helped millions of blogs get up and running, we know what works, and we want you to to know everything we know. This course provides all the fundamental skills and inspiration you need to get your blog started, an interactive community forum, and content updated annually.

Stop clicking sound from hard disk

It is annoying that the Western Digital hard drive in my Thinkpad continuously producing clicking sound by parking the reading head of the disk every few seconds. The behaviour is controlled by APM level of the hard drive which is accessed by

smartctl -x /dev/sda | grep APM

To disable the feature and stop parking reading head of the disk, we should set the APM level to 254 (Max Performance) or 255 (Disable). They both seems work but I don’t know which is better, so I choose to disable it completely. To do so, simply run hdparm:

hdparm -B 255 /dev/sda

I notice that the annoying sound disappears immediately. But it is not done yet! After reboot or sleep or hibernation, I find that the clicking sound returns and the APM level is set back to its default value.

So now, I have to run the above command every time I boot or resume from sleep/hibernation. I’m currently using Arch Linux and it uses systemd now and I’m going to create a service that is automatically run on boot and resume to run hdparm.

After some searching and consulting man pages of systemd, I finally get the job done with the following service file:

[Unit]
Description=Disable APM in /dev/sda
After=sleep.target

[Service]
Type=oneshot
ExecStart=/sbin/hdparm -B255 /dev/sda

[Install]
WantedBy=sleep.target basic.target

There are a few lines I want to explain a little.

Type=oneshot

The above line tells systemd that the service process will run and exit. Once it succeed and exit, don’t try to start it again.

WantedBy=sleep.target basic.target

This line says that the service should be run when the system is put to sleep or hibernate (by sleep.target altogether) and when the system is started (by basic.target).

After=sleep.target

Finally, this line let the service (although wanted by sleep.target) start after sleep.target, namely when waking up instead of going to sleep.

To make this service work, simply name it hdparm.service (of course you can name it anything_you_want.service) and put it in /usr/lib/systemd/system, and run the following command as root to enable and start it.

systemctl enable hdparm
systemctl start hdarm

Now, say bye-bye to the clicking sound!