A shell is a command-line interpreter that takes and executes commands, and in doing so, implements a programming language commonly referred to as “shell scripting.” Shell scripting is traditionally used by UNIX system administrators to automate the execution of system tasks, but developers also use shell scripts to create enterprise-level software installations, and that is where problems occur.
UNIX Installations: The Tar-Ball Archive
Most UNIX shell-based installations are constructed using a “tar-ball” (tar archive) paradigm. A tar file is delivered whose contents include a shell script and the files to be installed. For the most part the files are stored in a structure mimicking that of their final destination. This potentially means that a single copy (cp) command is invoked to copy the entire directory to the home directory. All necessary configurations are done with the help of commands such as “sed”, “awk”, and so on. The user interface provided is console only.
By and large, the above scenario is acceptable for simple applications. However, it becomes very hard to follow when even the most straightforward enterprise installations are attempted. If the installation you are attempting to construct is required to configure application servers, databases, or Web servers, shell-based installations are rarely a viable delivery option. This delivery mechanism becomes more difficult when you reanalyze how the tar archive is built. The configuration management team has to produce a directory structure that will mimic the final destination.
Not all UNIX environments are created equal. Some commands work in every UNIX environment; some do not. Sometimes commands such as “pidof” do not exist in environments other than Linux. Furthermore, some utilities do not share the same command-line arguments across all UNIX implementations. Besides, would you like to learn all the command-line options offered by UNIX commands such as “ls”? To account for portability, a great deal of code must be written.
Even though shell was never intended for large development, shell scripting is sometimes used to create large installation projects, such as enterprise installations. These installations need to directly communicate configuration settings to the product being installed. For shell-based installations, this is done using input files or the command line. Error handling, for the most part, is done using return codes.
Unfortunately, return codes are not always reliable. Sometimes a command may return 0 when in fact it should return anything but 0. Extra steps need to be taken to ensure reliability, which can turn into unnecessary complex if-statements that make interoperability all the more difficult.
When developing large-scale enterprise installations, you can expect more than one developer to work on the same code. Your plan, of course, is to develop reusable libraries that can be used across the installation system. However, shell was not designed with that in mind. Shell is for the most part used to automate administration tasks such as “empty the temp space” or “manage users.” It was designed to deal with low-level tasks, not code sharing.
Shell was written long before SGML technologies such as XML were available. Therefore, its abilities for parsing such formats are based on regular expressions. For the most part, regular expressions are hard to write and even harder to debug. To consider writing regular expressions for parsing XML would put you in the business of building XML parsers from scratch. Modern languages such as Java and C++ have XML parsers built-in.
To learn more, read the white paper Shell vs. Java: Overcoming the Challenges of Shell Scripting for UNIX Installations