Use arrays instead of strings for building up bash argument lists to pass to other commands

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the bash category.

Last Updated: 2024-11-21

I wanted to build up the arguments for a command using strings in a a bash script. I came up with the following code, which unfortunately didn't work:

buckets_and_options="s3://website s3://website-autobackup --profile backups"

function dry_run() {
  aws s3 sync --dryrun "$buckets_and_options"
  exit 0
}

The problem was that the aws command treated the entire string as a single argument, instead of 3 space-separated arguments. This is due to word-splitting being suppressed, something that happens when you quote a string, with the exception of "$@" and "${array[@]}"

After some Googling, I learned that, in general, it's a bad idea to demote a list of separate items into a single string, no matter whether it's a list of command line options or a list of pathnames.

The fix:

# I am using two arrays here to improve readability.
sync_options=(--profile backups)
buckets=(s3://website s3://website-autobackup)

function dry_run() {
  aws s3 sync --dryrun "${sync_options[@]}" "${buckets[@]}"
  exit 0
}