Make Bash script exit and print error message if users invoke the script incorrectly

25.4k views Asked by At

Script needed was

#!/bin/bash

# Check if there are two arguments
if [ $# -eq 2 ]; then
   # Check if the input file actually exists.
   if ! [[ -f "$1" ]]; then
     echo "The input file $1 does not exist."
     exit 1
   fi
else
   echo "Usage: $0 [inputfile] [outputfile]"
   exit 1
fi

# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "$1" > "$2"

Edit, the script has changed to

grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
if [ ! -f "$1" ]; then

  echo 'Usage: '
  echo
  echo './Scriptname inputfile > outputfile'
  exit 0

fi

invoking the script with no parameters gives no erros and sits blank

Usage: 

./Scriptname inputfile > outputfile

I have bit of code

grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*

This code pulls lines that have a single word on them and pumps the output to a new file, so for example

This is a multi word line
this
the above line is not
now
once again wrong

The output would be

This
now

The code works, users invoke the code using ./scriptname file > newfile

However, I am trying to expand the code to give users an error message if they invoke the script incorrectly.

For the error messange, I'm thinking of echoing something back like scriptname file_to_process > output_file.

I did try

if [incorrectly invoted unsure what to type]
echo $usage

exit 1
Usage="usage [inputfile] [>] [outputfile]

However I have had little luck. The code runs but does nothing if I invoke with just the script name. Also, if I invoke the script with just the scriptname and the input file, it will output the results instead of exiting with the error message.

Other ones I have tried are

if [ ! -n $1 ]; then

  echo 'Usage: '
  echo 
  echo './Scriptname inputfile > outputfile'
  exit 0

fi

Given replies I have received so far, my code now is

#!/bin/bash
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $*
if [ ! -f "$1" ]; then

  echo 'Usage: '
  echo 
  echo './Scriptname inputfile > outputfile'
  exit 0

fi

When invoking the script without an input file the script does nothing and has to be aborted with ctrl+c, still trying to get the echo of the invoke message.

4

There are 4 answers

2
doubleDown On BEST ANSWER

When you are invoking the script like ./scriptname file > newfile, the shell interprets file as the only argument to ./scriptname. This is because > is the standard output redirection operator.

I would like to propose 2 possible alternatives:


Alternative 1: Maybe you're can try passing it as 1 argument like this?

./scriptname 'file > newfile'

In that case one way to check the format would be

#!/bin/bash

# Check if the format is correct
if [[ $1 =~ (.+)' > '(.+) ]]; then
  # Check if the input file actually exists.
  if ! [[ -f "${BASH_REMATCH[1]}" ]]; then
    echo "The input file ${BASH_REMATCH[1]} does not exist!"
    exit 1
  fi
else
  echo "Usage: $0 \"[inputfile] [>] [outputfile]\""
  exit 1
fi

# Redirect standard output to the output file
exec > "${BASH_REMATCH[2]}"
# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "${BASH_REMATCH[1]}"

Note: If you are checking whether the arguments are valid or not, it's generally better to run commands only after the checking is done.


Alternative 2: Passing 2 arguments like

./scriptname file newfile

The script looks like this

#!/bin/bash

# Check if there are two arguments
if [ $# -eq 2 ]; then
   # Check if the input file actually exists.
   if ! [[ -f "$1" ]]; then
     echo "The input file $1 does not exist."
     exit 1
   fi
else
   echo "Usage: $0 [inputfile] [outputfile]"
   exit 1
fi

# Run the command on the input file
grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" "$1" > "$2"
2
Ansgar Wiechers On

I'd use parameter expansion for this:

inputfile=${1:?Usage: $(basename $0) inputfile > outputfile}

If the script is called without arguments (i.e. $1 is unset) the ${var:?error message} expansion causes the shell to display an error with the given message and exit. Otherwise the first argument is assigned to $inputfile.

1
Alvin Wong On

Try to add double quotes around $1 and use -f to check for exists and is normal file:

if [ ! -f "$1" ]; then

  echo 'Usage: '
  echo 
  echo './Scriptname inputfile > outputfile'
  exit 0

fi
1
higuaro On

Also you can check for the param count with $# and cat an usage message:

if [ ! $# -eq 1 ]; then
cat << EOF
    Usage:

    $0 'input_file' > output_file
EOF
    exit 1
fi