[validation] Validating parameters to a Bash script

I came up with a basic one to help automate the process of removing a number of folders as they become unneeded.

#!/bin/bash
rm -rf ~/myfolder1/$1/anotherfolder
rm -rf ~/myfolder2/$1/yetanotherfolder
rm -rf ~/myfolder3/$1/thisisafolder

This is evoked like so:

./myscript.sh <{id-number}>

The problem is that if you forget to type in the id-number (as I did just then), then it could potentially delete a lot of things that you really don't want deleted.

Is there a way you can add any form of validation to the command line parameters? In my case, it'd be good to check that a) there is one parameter, b) it's numerical, and c) that folder exists; before continuing with the script.

This question is related to validation bash shell

The answer is


The sh solution by Brian Campbell, while noble and well executed, has a few problems, so I thought I'd provide my own bash solution.

The problems with the sh one:

  • The tilde in ~/foo doesn't expand to your homedirectory inside heredocs. And neither when it's read by the read statement or quoted in the rm statement. Which means you'll get No such file or directory errors.
  • Forking off grep and such for basic operations is daft. Especially when you're using a crappy shell to avoid the "heavy" weight of bash.
  • I also noticed a few quoting issues, for instance around a parameter expansion in his echo.
  • While rare, the solution cannot cope with filenames that contain newlines. (Almost no solution in sh can cope with them - which is why I almost always prefer bash, it's far more bulletproof & harder to exploit when used well).

While, yes, using /bin/sh for your hashbang means you must avoid bashisms at all costs, you can use all the bashisms you like, even on Ubuntu or whatnot when you're honest and put #!/bin/bash at the top.

So, here's a bash solution that's smaller, cleaner, more transparent, probably "faster", and more bulletproof.

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. Notice the quotes around $1 in the rm statement!
  2. The -d check will also fail if $1 is empty, so that's two checks in one.
  3. I avoided regular expressions for a reason. If you must use =~ in bash, you should be putting the regular expression in a variable. In any case, globs like mine are always preferable and supported in far more bash versions.

I would use bash's [[:

if [[ ! ("$#" == 1 && $1 =~ ^[0-9]+$ && -d $1) ]]; then 
    echo 'Please pass a number that corresponds to a directory'
    exit 1
fi

I found this faq to be a good source of information.


Old post but I figured i could contribute anyway.

A script is arguably not necessary and with some tolerance to wild cards could be carried out from the command line.

  1. wild anywhere matching. Lets remove any occurrence of sub "folder"

    $ rm -rf ~/*/folder/*
    
  2. Shell iterated. Lets remove the specific pre and post folders with one line

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
    
  3. Shell iterated + var (BASH tested).

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
    

You can validate point a and b compactly by doing something like the following:

#!/bin/sh
MYVAL=$(echo ${1} | awk '/^[0-9]+$/')
MYVAL=${MYVAL:?"Usage - testparms <number>"}
echo ${MYVAL}

Which gives us ...

$ ./testparams.sh 
Usage - testparms <number>

$ ./testparams.sh 1234
1234

$ ./testparams.sh abcd
Usage - testparms <number>

This method should work fine in sh.


Use '-z' to test for empty strings and '-d to check for directories.

if [[ -z "$@" ]]; then
    echo >&2 "You must supply an argument!"
    exit 1
elif [[ ! -d "$@" ]]; then
    echo >&2 "$@ is not a valid directory!"
    exit 1
fi

Use set -u which will cause any unset argument reference to immediately fail the script.

Please, see the article: Writing Robust Bash Shell Scripts - David Pashley.com.


Not as bulletproof as the above answer, however still effective:

#!/bin/bash
if [ "$1" = "" ]
then
  echo "Usage: $0 <id number to be cleaned up>"
  exit
fi

# rm commands go here

one liner Bash argument validation, with and without directory validation

Here are some methods that have worked for me. You can use them in either the global script namespace (if in the global namespace, you can't reference the function builtin variables)

quick and dirty one liner

: ${1?' You forgot to supply a directory name'}

output:

./my_script: line 279: 1: You forgot to supply a directory name

Fancier - supply function name and usage

${1? ERROR Function: ${FUNCNAME[0]}() Usage: " ${FUNCNAME[0]} directory_name"}

output:

./my_script: line 288: 1:  ERROR Function: deleteFolders() Usage:  deleteFolders directory_name

Add complex validation logic without cluttering your current function

Add the following line within the function or script that receives the argument.

: ${1?'forgot to supply a directory name'} && validate $1 || die 'Please supply a valid directory'

You can then create a validation function that does something like

validate() {

    #validate input and  & return 1 if failed, 0 if succeed
    if [[ ! -d "$1" ]]; then
        return 1
    fi
}

and a die function that aborts the script on failure

die() { echo "$*" 1>&2 ; exit 1; }

For additional arguments, just add an additional line, replicating the format.

: ${1?' You forgot to supply the first argument'}
: ${2?' You forgot to supply the second argument'}

The man page for test (man test) provides all available operators you can use as boolean operators in bash. Use those flags in the beginning of your script (or functions) for input validation just like you would in any other programming language. For example:

if [ -z $1 ] ; then
  echo "First parameter needed!" && exit 1;
fi

if [ -z $2 ] ; then
  echo "Second parameter needed!" && exit 2;
fi

Examples related to validation

Rails 2.3.4 Persisting Model on Validation Failure Input type number "only numeric value" validation How can I manually set an Angular form field as invalid? Laravel Password & Password_Confirmation Validation Reactjs - Form input validation Get all validation errors from Angular 2 FormGroup Min / Max Validator in Angular 2 Final How to validate white spaces/empty spaces? [Angular 2] How to Validate on Max File Size in Laravel? WebForms UnobtrusiveValidationMode requires a ScriptResourceMapping for jquery

Examples related to bash

Comparing a variable with a string python not working when redirecting from bash script Zipping a file in bash fails How do I prevent Conda from activating the base environment by default? Get first line of a shell command's output Fixing a systemd service 203/EXEC failure (no such file or directory) /bin/sh: apt-get: not found VSCode Change Default Terminal Run bash command on jenkins pipeline How to check if the docker engine and a docker container are running? How to switch Python versions in Terminal?

Examples related to shell

Comparing a variable with a string python not working when redirecting from bash script Get first line of a shell command's output How to run shell script file using nodejs? Run bash command on jenkins pipeline Way to create multiline comments in Bash? How to do multiline shell script in Ansible How to check if a file exists in a shell script How to check if an environment variable exists and get its value? Curl to return http status code along with the response docker entrypoint running bash script gets "permission denied"