Lably
Skip to content

Shell script

Script written for an operating system shell

  • 15 min read
  • 7 chapters
Using vi to edit "/etc/rc.firewall" in FreeBSD 10. Running in a QEMU VM.
Huihermit · CC0

Related on Lably

Through Computer program, Unix shell and Operating system

The term is also used more generally to mean the automated mode of running an operating system shell. Different operating systems each use a particular name for these functions. All Unix-like systems include at least one POSIX shell, typically either bash or the zsh compatibility mode.

Capabilities

Comments

Comments are ignored by the shell. They typically begin with the hash symbol (#), and continue until the end of the line.

Configurable choice of scripting language

The shebang, or hash-bang, is a special kind of comment which the system uses to determine what interpreter to use to execute the file. The shebang must be the first line of the file, and start with "#!". In Unix-like operating systems, the characters following the "#!" prefix are interpreted as a path to an executable program that will interpret the script.

Shortcuts

A shell script can provide a convenient variation of a system command where special environment settings, command options, or post-processing apply automatically, but in a way that allows the new script to still act as a fully normal Unix command.

One example would be to create a version of ls, the command to list files, giving it a shorter command name of l, which would be normally saved in a user's bin directory as /home/username/bin/l, and a default set of command options pre-supplied.

<span id="mwQQ"></span><span class="ch" id="mwQg">#!/bin/sh</span> <span class="nv" id="mwQw">LC_COLLATE</span><span class="o" id="mwRA">=</span>C<span class="w" id="mwRQ"> </span>ls<span class="w" id="mwRg"> </span>-FCas<span class="w" id="mwRw"> </span><span class="s2" id="mwSA">"</span><span class="nv" id="mwSQ">$@</span><span class="s2" id="mwSg">"</span>

Here, the first line uses a shebang to indicate which interpreter should execute the rest of the script, and the second line makes a listing with options for file format indicators, columns, all files (none omitted), and a size in blocks. The LC_COLLATE=C sets the default collation order to not fold upper and lower case together, not intermix dotfiles with normal filenames as a side effect of ignoring punctuation in the names (dotfiles are usually only shown if an option like -a is used), and the "$@" causes any parameters given to l to pass through as parameters to ls, so that all of the normal options and other syntax known to ls can still be used.

The user could then simply use l for the most commonly used short listing.

Beyond simply providing a set of default for a command, scripts provide for running multiple commands automatically upon script invocation. This one, ll, merely clears the terminal of all other text before running a listing command.

<span id="mwWQ"></span><span class="ch" id="mwWg">#!/bin/sh</span> clear ls<span class="w" id="mwWw"> </span>-al<span class="w" id="mwXA"> </span><span class="s2" id="mwXQ">"</span><span class="nv" id="mwXg">$@</span><span class="s2" id="mwXw">"</span>

As in the prior example, this ls -al invocation lists the files and directories that are in the directory from which the script is being run, or any directories given after ll on the command line, which are automatically substituted in for the "$@". The -la command options here cause "all" directory contents to be shown in "long" format.

Batch jobs

Shell scripts allow several commands that would be entered manually at a command-line interface to be executed automatically, and without having to wait for a user to trigger each stage of the sequence. For example, in a directory with three C source code files, rather than manually running the four commands required to build the final program from them, one could instead create a script for POSIX-compliant shells, here named build and kept in the directory with them, which would compile them automatically:

<span id="mwaw"></span><span class="ch" id="mwbA">#!/bin/sh</span> <span class="nb" id="mwbQ">echo</span><span class="w" id="mwbg"> </span><span class="s1" id="mwbw">'compiling...'</span> cc<span class="w" id="mwcA"> </span>-c<span class="w" id="mwcQ"> </span>foo.c cc<span class="w" id="mwcg"> </span>-c<span class="w" id="mwcw"> </span>bar.c cc<span class="w" id="mwdA"> </span>-c<span class="w" id="mwdQ"> </span>qux.c cc<span class="w" id="mwdg"> </span>-o<span class="w" id="mwdw"> </span>myprog<span class="w" id="mweA"> </span>foo.o<span class="w" id="mweQ"> </span>bar.o<span class="w" id="mweg"> </span>qux.o <span class="nb" id="mwew">echo</span><span class="w" id="mwfA"> </span><span class="s1" id="mwfQ">'done.'</span>

The script would allow a user to save the file being edited, pause the editor, and then just run ./build to create the updated program, test it, and then return to the editor. Since the 1980s or so, however, scripts of this type have been replaced with utilities like make which are specialized for building programs.

Generalization

Simple batch jobs are not unusual for isolated tasks, but using shell loops, tests, and variables provides much more flexibility to users. A POSIX sh script to convert JPEG images to PNG images, where the image names are provided on the command-line—possibly via wildcards—instead of each being listed within the script, can be created with this file, typically saved in a file like /home/username/bin/jpg2png

<span id="mwhw"></span><span class="ch" id="mwiA">#!/bin/sh</span> <span class="c1" id="mwiQ"># set $img to each of the jpgs in the command arguments</span> <span class="k" id="mwig">for</span><span class="w" id="mwiw"> </span>img<span class="w" id="mwjA"> </span><span class="k" id="mwjQ">in</span><span class="w" id="mwjg"> </span><span class="s2" id="mwjw">"</span><span class="nv" id="mwkA">$@</span><span class="s2" id="mwkQ">"</span><span class="p" id="mwkg">;</span><span class="w" id="mwkw"> </span><span class="k" id="mwlA">do</span><span class="w" id="mwlQ"> </span><span class="c1" id="mwlg"># for each given image...</span> <span class="w" id="mwlw"> </span><span class="c1" id="mwmA"># use ${img%.jpg} to get $img w/o the .jpg</span> <span class="w" id="mwmQ"> </span><span class="nv" id="mwmg">png</span><span class="o" id="mwmw">=</span><span class="si" id="mwnA">${</span><span class="nv" id="mwnQ">img</span><span class="p" id="mwng">%.jpg</span><span class="si" id="mwnw">}</span>.png<span class="w" id="mwoA"> </span><span class="c1" id="mwoQ"># filename for successful PNG </span> <span class="w" id="mwog"> </span><span class="nv" id="mwow">tmp</span><span class="o" id="mwpA">=</span>tmp.png<span class="w" id="mwpQ"> </span><span class="c1" id="mwpg"># filename before sure it worked</span> <span class="w" id="mwpw"> </span><span class="nb" id="mwqA">echo</span><span class="w" id="mwqQ"> </span><span class="s2" id="mwqg">"converting '</span><span class="nv" id="mwqw">$img</span><span class="s2" id="mwrA">'..."</span> <span class="w" id="mwrQ"> </span><span class="c1" id="mwrg"># Use ImageMagick's "convert" to write our new PNG</span> <span class="w" id="mwrw"> </span><span class="c1" id="mwsA"># image into our $tmp file - which may fail!</span> <span class="w" id="mwsQ"> </span><span class="k" id="mwsg">if</span><span class="w" id="mwsw"> </span>convert<span class="w" id="mwtA"> </span><span class="s2" id="mwtQ">"</span><span class="nv" id="mwtg">$img</span><span class="s2" id="mwtw">"</span><span class="w" id="mwuA"> </span><span class="s2" id="mwuQ">"</span><span class="nv" id="mwug">$tmp</span><span class="s2" id="mwuw">"</span><span class="w" id="mwvA"> </span><span class="p" id="mwvQ">;</span><span class="w" id="mwvg"> </span><span class="k" id="mwvw">then</span><span class="w" id="mwwA"> </span><span class="c1" id="mwwQ"># if it works...</span> <span class="w" id="mwwg"> </span><span class="c1" id="mwww"># convert worked (since we're in here)</span> <span class="w" id="mwxA"> </span><span class="c1" id="mwxQ"># (this script assumes "mv" always succeeds)</span> <span class="w" id="mwxg"> </span>mv<span class="w" id="mwxw"> </span><span class="s2" id="mwyA">"</span><span class="nv" id="mwyQ">$tmp</span><span class="s2" id="mwyg">"</span><span class="w" id="mwyw"> </span><span class="s2" id="mwzA">"</span><span class="nv" id="mwzQ">$png</span><span class="s2" id="mwzg">"</span><span class="w" id="mwzw"> </span><span class="c1" id="mw0A"># rename tmp file to target png</span> <span class="w" id="mw0Q"> </span><span class="nb" id="mw0g">echo</span><span class="w" id="mw0w"> </span><span class="s2" id="mw1A">"converted </span><span class="nv" id="mw1Q">$jpg</span><span class="s2" id="mw1g"> to </span><span class="nv" id="mw1w">$png</span><span class="s2" id="mw2A">"</span> <span class="w" id="mw2Q"> </span><span class="k" id="mw2g">else</span> <span class="w" id="mw2w"> </span><span class="c1" id="mw3A"># convert failed (since we're in here)</span> <span class="w" id="mw3Q"> </span><span class="c1" id="mw3g"># tell the user about it</span> <span class="w" id="mw3w"> </span><span class="nb" id="mw4A">echo</span><span class="w" id="mw4Q"> </span><span class="s2" id="mw4g">"conversion to </span><span class="nv" id="mw4w">$png</span><span class="s2" id="mw5A"> failed, result in </span><span class="nv" id="mw5Q">$tmp</span><span class="s2" id="mw5g">"</span> <span class="w" id="mw5w"> </span><span class="nb" id="mw6A">exit</span><span class="w" id="mw6Q"> </span><span class="m" id="mw6g">1</span><span class="w" id="mw6w"> </span><span class="c1" id="mw7A"># exit, the 1 indicates an error</span> <span class="w" id="mw7Q"> </span><span class="k" id="mw7g">fi</span><span class="w" id="mw7w"> </span><span class="c1" id="mw8A"># the end of the "if" test construct</span> <span class="k" id="mw8Q">done</span><span class="w" id="mw8g"> </span><span class="c1" id="mw8w"># the end of the "for" loop</span> <span class="nb" id="mw9A">echo</span><span class="w" id="mw9Q"> </span><span class="s1" id="mw9g">'all conversions successful'</span> <span class="c1" id="mw9w"># now the script exits with 0 (default) showing success</span>

The jpg2png command can then be run on an entire directory full of JPEG images with just /home/username/bin/jpg2png *.jpg

Programming

Many modern shells also supply various features usually found only in more sophisticated general-purpose programming languages, such as control-flow constructs, variables, comments, arrays, subroutines and so on. With these sorts of features available, it is possible to write reasonably sophisticated applications as shell scripts. However, they are still limited by the fact that most shell languages have little or no support for data typing systems, classes, threading, complex math, and other common full language features, and are also generally much slower than compiled code or interpreted languages written with speed as a performance goal.

The standard Unix tools sed and awk provide extra capabilities for shell programming; Perl can also be embedded in shell scripts as can other scripting languages like Tcl. Perl and Tcl come with graphics toolkits as well.

Sign in to keep reading

6 more chapters are free with a Lably account.

More in Computing and communication