5

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.

Ausghostdog
  • 178
  • 1
  • 3
  • 13
  • Are you using `/bin/bash` or `/bin/sh`? – Alvin Wong Oct 19 '12 at 02:45
  • Use `"$@"` rather than `$*` on the command line; that preserves the spaces in file name arguments with spaces. If you use `grep -P -e "...your regex..."` with the `-e` preceding the expression, then users can type things like `-n` or `-l` as an option on the command line, and `grep` will modify its behaviour appropriately. (On systems where GNU `getopt()` is used to permute arguments, you may not need to specify `-e`; I am not keen on that feature.) – Jonathan Leffler Oct 19 '12 at 04:42
  • Ok thnaks for that I will change the $@ now – Ausghostdog Oct 19 '12 at 04:57
  • Instead of `[ ! -n $1 ]` use `[ -z "$1" ]`. Also put the grep after the parameter count test, not before. If it's before and there is no parameter it will sit there waiting for you to type input on stdin. To experiment, try `grep x` at the command line without a file name and type in some lines with and without x in them, then ^D to exit. – James Waldby - jwpat7 Oct 19 '12 at 06:09

4 Answers4

3

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"
doubleDown
  • 8,048
  • 1
  • 32
  • 48
  • Thank you, the only issue is the output is `Usage: $0 [inputfile] [>] [outputfile]` so I would just need to add another else after the final fi and an exit 0 correct? – Ausghostdog Oct 19 '12 at 10:46
  • The script will run `grep` and if `grep` runs successfully, the script will exit with status 0, so you don't actually need `exit 0` in the script (if I am understanding what you are saying...) – doubleDown Oct 19 '12 at 23:51
3

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.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Good question - for simple scripts it is an elegant one-line solution and works well for me, thanks! – randomsock May 25 '17 at 11:21
  • Also seems you can split the error message over lines in your script and the output will reflect the same line-breaks, if you needed to format the Usage more clearly – randomsock May 25 '17 at 11:36
1

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
Alvin Wong
  • 12,210
  • 5
  • 51
  • 77
  • Ok I tired that using `grep -P "^[\s]*[0-9A-Za-z-]+.?[\s]*$" $* if [ ! -f "$1" ]; then echo 'Usage: ' echo echo './Scriptname inputfile > outputfile' exit 0 fi` The code when I invoked it just sat there an did nothing, I tried invoking with just the script name, have I missed something in using the code. – Ausghostdog Oct 19 '12 at 02:54
1

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
higuaro
  • 15,730
  • 4
  • 36
  • 43