Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 204
nickf Avatar asked Mar 31 '09 00:03

nickf


People also ask

What does [- Z $1 mean in bash?

$1 means an input argument and -z means non-defined or empty. You're testing whether an input argument to the script was defined when running the script. Follow this answer to receive notifications.

How do you check if a variable is defined in bash?

To find out if a bash variable is defined: Return true if a bash variable is unset or set to the empty string: if [ -z ${my_variable+x} ]; Also try: [ -z ${my_bash_var+y} ] && echo "\$my_bash_var not defined"


2 Answers

#!/bin/sh die () {     echo >&2 "$@"     exit 1 }  [ "$#" -eq 1 ] || die "1 argument required, $# provided" echo $1 | grep -E -q '^[0-9]+$' || die "Numeric argument required, $1 provided"  while read dir  do     [ -d "$dir" ] || die "Directory $dir does not exist"     rm -rf "$dir" done <<EOF ~/myfolder1/$1/anotherfolder  ~/myfolder2/$1/yetanotherfolder  ~/myfolder3/$1/thisisafolder EOF 

edit: I missed the part about checking if the directories exist at first, so I added that in, completing the script. Also, have addressed issues raised in comments; fixed the regular expression, switched from == to eq.

This should be a portable, POSIX compliant script as far as I can tell; it doesn't use any bashisms, which is actually important because /bin/sh on Ubuntu is actually dash these days, not bash.

like image 183
Brian Campbell Avatar answered Oct 05 '22 07:10

Brian Campbell


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.
like image 21
lhunath Avatar answered Oct 05 '22 09:10

lhunath