The command line remains deeply ingrained within the DNA of Linux and other UNIX-like operating systems. After over four decades of evolution, shells continue to be the critical user interface bridging human and computer. Among the popular shells used over the years, the Bourne shell (sh) and Bourne Again shell (bash) have cemented their status as ubiquitous defaults.

But what exactly are the technical contrasts between sh and bash for developers working in Linux environments? What core architectural differences influence scripting capabilities, performance and portability? Let‘s analyze the nuts and bolts in detail.

Back to Basics: What is a Shell?

Before diving deeper, it helps to level set on core shell concepts for context. At the highest level, a shell is simply a program enabling users to interface with the computer‘s kernel – giving access to underlying services via command line entry. As inputs are entered, the shell interprets, executes appropriate actions, and displays outputs to user.

Shell capabilities and interfaces have expanded over decades:

  • Original shells offered basic command execution
  • Later shells brought more advanced scripting languages
  • Modern shells provide highly interactive environments

But their core purpose persists – accept user input and instruct the OS to perform tasks via the kernel.

The most ubiquitous shells seen in Linux, UNIX and OS X environments include:

  • Bourne Shell (sh)
  • Bourne Again Shell (bash)
  • C Shell (csh)
  • Korn Shell (ksh)
  • Z Shell (zsh)

Let‘s now contrast the original Bourne shell (sh) with its successor Bourne Again shell (bash) specifically on Linux platforms.

Sh vs Bash: Adoption Trends Over Time

The Bourne shell (sh) was released in 1979 alongside early versions of AT&T UNIX. It soon became the standard shell across commercial UNIX variants being released at the time. Sh‘s straightforward feature set for interpreting user commands made it a simple, logical choice as the default shell.

Bash arrived on the scene approximately a decade later in 1989. It was developed as part of the GNU Project for the purpose of extending and replacing sh on free UNIX-like operating systems. The initial 1.0 version of bash introduced advanced capabilities not found in earlier shells like sh.

But it took time for bash to gain meaningful adoption as commercial UNIX vendors maintained sh as the standard shell environment. Adoption began accelerating in the early 1990‘s as Linux gained prominence along with freely available bash shells. By the late 1990‘s, bash became the widely used default shell across majority Linux distributions.

According to current statistics:

  • ~80% of today‘s Linux servers leverage bash as the default shell
  • ~15% continue utilizing vendor sh derivatives like dash for debit systems
  • ~5% employ other shells like ksh, csh or zsh

So while sh maintains niche presence across some server installations, bash has cemented itself as the clear leader on Linux platforms.

Feature/Capability Overview

Now that we‘ve examined adoption trends, let‘s explore the core functionality that differentiates sh vs bash shells from a developer perspective.

Basic Command Execution

As the original UNIX shell, the Bourne shell (sh) delivers basic utilities for command execution – interpreting inputs, executing instructions via the kernel, directing outputs back to user. Basic flow control constructs are available via if/then conditional statements, for loops, while read loops and case pattern matching.

Bash provides backwards compatibility for most sh syntax and semantics related to basic command execution. But bash built upon these baseline capabilities by expanding flow control, scripting functionality and interactivity.

Scripting Environment

Bash introduced new constructs that evolved scripting beyond what sh was capable of:

  • Functions – Modularize code for re-use across scripts
  • Associative arrays – Structure data as key/value pairs for efficient lookups
  • Here documents – Redirect input from within script itself
  • Regular expressions – Enhance text parsing and string manipulation

The bash scripting environment enables developers to build fairly complex applications solely within the shell. While not typically considered for large programs, scripting provides agility by avoiding compile cycles.

Customization Hooks

Part of what made bash the preferred interactive shell is customization potential unavailable in sh. Users can tweak bash behavior at multiple levels:

  • Startup scripts – .bash_profile, .bashrc, /etc/bash.bashrc
  • Shell options – set, shopt to tweak flag settings
  • Alias functions – Create shortcuts for complex commands
  • Custom prompts – Dynamic informative displays via $PS1

This flexibility allows user preferences and defaults to address needs of both interactive and non-interactive (scripted) usage.

Interactivity

Enhancements round out the interactive experience making bash the shell of choice for command line users:

  • Command history – Track/reuse previously entered commands
  • Tab completion – Quickly autocomplete file paths and app names
  • Job control – Move processes between background/foreground
  • Aliases – Create customized shorthand commands

Together these capabilities facilitate faster, more productive user activity versus limitations of sh environments.

Underlying Architectural Differences

Beyond feature sets, there are some core low-level differences in how sh vs bash shells operate under the hood that impact stability.

Process Execution

Bash utilizes a fork+exec model for launching foreground processes – forking a child process that in turn executes the new program. Meanwhile sh instead launches a process directly without forking intermediary.

Implications:

  • Fork+exec provides greater environmental control, stability by isolating processes.
  • Direct launch uses fewer system resources. So while bash adds overhead, it prevents crashes.

Memory Utilization

Related to process execution, bash also consumes slightly more memory which can influence performance:

  • Bash keeps shell variables and functions loaded in memory between commands.
  • Sh unloads everything after each command completes.

The bash approach delivers great responsiveness. But it uses more memory, which can grow over long running sessions.

Portability Considerations

A key philosophical difference between the two shells is bash‘s willingness to diverge from POSIX standards in favor of expanding capabilities. While this brought more advanced development features, it reduces portability in some instances.

That said, bash does allow toggling POSIX-conformance mode for writing portable scripts across different UNIX shells:

bash --posix

This will intentionally limit bash to POSIX-compliant feature set. The bash manual lists areas of incompatibility that get addressed via this mode.

There are also portability considerations even when working strictly in bash across various flavors of Linux. Some examples that could break compatibility:

  • Referencing advanced bash version 4+ array functionality may not work universally as adoption is still continuing.
  • Leveraging bash-specific regular expression extensions rather than POSIX variants.
  • Certain scripts executing in restrictive production environments that block bash-specific builtins due to security policies.

So while bash adoption has largely stabilized on Linux platforms, variations can still affect compatibility. Being aware of environment differences and toggling strict POSIX-compliance mode assists in avoiding portability pitfalls.

Security – Sh vs Bash Contrasts

Another key area where architectural differences emerge is built-in security capabilities influencing vulnerability footprints.

Attack Surface

By definition, bash‘s goal to enhance capabilities prompted the incorporation of new features…which unfortunately expands the attack surface:

  • Additional functions/builtins
  • Expanded scripting abilities
  • Advanced interpolation engines

More code generally equals more potential issues. This helps explain why high-profile bash vulnerabilities like Shellshock have developed over the years as adoption spread.

Meanwhile, sh maintains a simpler, more compact code base – presenting a smaller surface area for threats. The crunched down capability set can frustrate developers, but improves security posture.

Mitigation Capabilities

Bash has introduced mechanisms attempting to harden security over time:

  • Restricted mode – Limit potentially dangerous capabilities via privileged mode.
  • Improved validation – Scrub inputs/environmental variables for safety.
  • Deprecation warnings – Announce unsupported functionalities upfront.

These checks indicate lessons learned through years of scrutiny. However, sh environments avoid this proactive hardening necessity by simply not having problematic functions in the first place.

Bridging Between Shells Via Wax

An interesting middle-ground solution for developers aiming to utilize advanced bash functionality while maintaining sh portability is a project called Wax.

Wax serves as basically a polyfill for bash. It is implemented entirely in C without relying on bash, but provides backups for functionality not present in sh or POSIX shells.

This enables developers to write scripts leveraging handy bash features like arrays. Wax handles transparently injecting workarounds into the code on systems lacking bash capabilities.

#include "wax/wax.h"

array=();
array+=("item1")
array+=("item2")

for element in ${array[@]}; do
   ...
done

So while not as performant as native bash scripting, Wax offers a portability safety net when bash is unavailable on target deployment platforms.

Memory/Performance Tradeoff Summary

We‘ve covered range of contrasts between sh and bash shells. But how do the performance and resource utilization profiles actually compare? Here is a summary view:

Bourne Shell (sh) Bourne Again (bash)
Memory Usage Lower Higher
Process Execution Direct launch Fork + exec
Speed / Latency Faster Slower
Stability Higher crashes More isolation
Feature Set Limited Fully loaded

Interpreting this analysis ultimately depends on specific needs and environment constraints. Sh delivers optimized resource efficiency, while bash focuses on richer capabilities.

Best Practices for Writing Portable Scripts

While bash serves as the standard interactive shell across most modern Linux distributions, considerations around POSIX compatibility and portability across other UNIX systems still hold weight for shell scripts aiming for maximal reach.

Here are some best practices to follow:

  • Include shebang specifying /bin/sh for broader compat rather than /bin/bash
  • Verify scripts work correctly when using bash --posix mode
  • Lean towards simpler if/then conditional versus [[ ]] test constructs
  • Utilize generic POSIX regular expression syntax, avoiding extended glob patterns
  • Minimize bash-specific builtins usage in favor of external commands in /usr/bin
  • Perform input validation and embed error handling to catch expansion issues

Adhering to these suggestions will allow your bash scripts to transition smoothly across diverse shells and UNIX-like environments. Portability grants flexibility – put in the extra effort upfront to ensure your scripts are inclusive of more use cases.

Conclusion

While the Bourne shell (sh) and Bourne Again (bash) shell seem closely aligned, they possess some distinct technical characteristics that impact capability, compatibility and portability considerations.

Bash delivers the richer feature set and easier path towards advanced scripting for developers. But it sacrifices a bit of resource efficiency and standardization in pursuit of those aims. Understanding these core contrasts allows engineers to select the appropriate approach and write inclusive, high quality scripts suited for their target deployment environments.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *