In this chapter, I’ll discuss Bash functions, how to create them and how to use them.

Bash functions: Functions are a good way to isolate common code that can be called from multiple other places. It’s also very useful when imported into the context of your command shell, to give you an extra set of commands. So in my case, the Object Storage library is a lot of functions, that when imported into the current shell can be used in isolation to (for example) download an Object. Of course in the build code, it’s a lot easier (and more readable) to glue a lot of these functions together to accomplish a specific task.

As an example, one of my meta-data files contains the directory name of the VM bits that need to be downloaded for a given demo. In this case a function to enumerate a directory is used, and a function to download an object is used for each of those object paths returned by the enumerator.

The three functions I use are:

  • SLObjectStorageGetDirList: Takes an Object Storage directory path, and can enumerate all objects under that path.
  • SLObjectStorageDownloadFile: Downloads a single object.
  • SLObjectStorageDownloadFiles: Uses the above two functions, to enumerate a path and download all the objects in that path.

The following snippet shows roughly how the SLObjectStorageDownloadFiles function can be used to download all objects in all paths specified in the file /ctx/scripts/custom/list_swift_packages.

The list_swift_packages files, for example, may contain the following lines:


The “do” loop shown below, will take each line of the file as a valid path, and download all objects contained in that path from a specific Object Storage region, into the current directory.

cat /ctx/scripts/custom/list_swift_packages | while read dname
if ! (
if ! SLObjectStorageDownloadFiles “$dname” $region …
exit 1

As noted in previous chapters, because a function is being called within a “do” loop, a new shell will be pushed by the current shell. Here I show that I explicitly push a new shell since this has implications for error handling. Notice how I can test the pushing of the new shell, and if non-zero break out of the “do” loop if ! (… Syntax here is not pretty, so in such cases try your best to make it clear with use of indents and comments.

Remembering from previous chapters: Object Storage has no concept of directories, although object names can have a separator that luckily defaults to a “/”. The SLObjectStorageDownloadFile and SLObjectStorageDownloadFiles functions downloads object names as a path names locally, so you will end up with a directory structure like ./democenter-files/workloads/… on the local disk.

How to create a function: Life with the Bash shell is simple! All you need do is start typing in the function from the CLI! In practice, you’ll have a separate text file that will be populated with the functions. These functions can then be imported into the context of your current shell for use by you. Or they can be imported into the context of a running script, for use in that script. Either way the syntax for importing the function is the same. If, for example, the path to your function library file is:


…then to import the function library, do the following:

. /ctx/scripts/common/ctxs_swift_imports

(Note the “.” followed by a space).

Dot space: The “dot space” idiom in Bash is used to run a shell script without pushing a new shell (and hence process) to run it. The script is run in the context of the current shell. In the above example, the file ctxs_swift_imports contains only function definitions. Running the file as a script would work, but the functions would be defined in a sub-shell, which would disappear after the script had finished. The “dot space” idiom is not really an “import” function as such, although it appears to work that way when working with a file that only contains function definitions. Generally, this idiom is used to isolate functions and common shell variables in a separate script file, and to run that script in the context of the current shell, thereby preserving any of the variables (including functions) that may have been declared in there.

Syntactically, the general form of a function definition is like this:

‘ Some normal bash statements here


function MyFirstFunction()
‘ Some normal bash statements here

The function keyword is not needed, but is there for clarity. The () should always be empty, and the body of the function should be between the {}. I won’t go into all the rules of engagement here, but sufficient to say that running a function is a bit like running a shell script in a sub shell (see below for clarification).

Example: Run the date command passing all arguments passed to the function through to the date command. Also define a local variable stat whose context is only within the function. Function can return a value for the callers $? special variable.

function MyFirstFunction()
local stat
if date ${*}
return $stat

Once the function has been defined in the context of the current shell or script (by literally typing the above in the CLI), calling it is easy:

Tue Mar  6 12:20:41 CST 2018

MyFirstFunction +’%D’

if ! MyFirstFunction
‘ Some error
Tue Mar  6 12:20:41 CST 2018

echo $mydate
Tue Mar  6 12:20:41 CST 2018

All the rules of redirection and running the function in the background can apply as well.

If running a function is so much like running a script, why don’t I place the functional parts in a separate script? I could place each functional part in its own script file in a directory, and add its path to the $PATH variable. There are differences:

  • Non-exported variables in the context of your shell are also available in the context of the function you call. Also, altering exported and non-exported variables in the function, will be reflected back in the caller. If you don’t want this, then declare variables as local to the function. With a separate script file, you only have access to exported variables, and then altering them will have not affect the parent’s copy of that same exported variable (don’t ask me; it’s been that way since the days of Kernighan & Ritchie).
  • To run a script in the same way a function runs, you would have to run the script in the context of the current shell using the “dot space” idiom talked about above.
  • You can have multiple functions in one file, whereas with scripts files, you will have to have a separate script file for each functional unit.

Of course there comes a time where you have to have a script that “glues” all these functions together in some meaningful manner.