2

I am effectively making a recycling bin via some scripts I made.

The first script is pretty much an alternative to the rm command (instead of actually deleting a file, it moves it to a deleted folder).

I've managed to allow the script to move multiple files to the deleted folder:

sh moveToBin file1 file2 fil3 (similar to: rm file1 file2 file3)

The start of my first script is:

#!/bin/bash
for param in "$@"
do
..
..
(main part of my code)

One by one, each parameter (file) is moved to the deleted folder. I am now trying to incorporate adding switch -parameters, but I'm not quite sure how to incorporate that.

The above works for sh moveToBin file1 file2 file3 but how do I incorporate the possibility that the first argument (only the first) COULD be a switch -i (ask to delete), -v (confirm deletion), -iv (ask to delete then confirm deletion).

Hence the switch only applies to $1. I tried out something called getopts but I'm not familiar with the use. Once a switch is used, this applies to $2 onwards i.e.

sh moveToBin -i file1 file2

this asks to delete file1, and after I decide, it then asks to delete file2 I thought of something like this, but I doubt it will work. any help?

counter=1
for param in "$@"
do
       while [[ if $param = "-*" && counter -eq1]];
       do 
              getopts "iv" arg;
              case "$arg" in
                  i) read -p "want to delete $param ?" ans
                     if [ ans =~ ^[Y][y] ] 
                     then 
                          #run main code for that param value              
                     fi;;
                  v) #run main code for that param value 
                     echo "file @param deleted";;
              esac
              counter=$((counter+1))
              continue
       done

      #run main code for that param value
done

The while loop condition means that it is the first parameter and that this parameter starts with a hyphen.

  • The getopts builtin to bash might help with option parsing. – thrig Aug 20 '15 at 21:45
  • I'm not familiar with what you mean, could you elaborate? apologies I started learning unix last monday – user3120554 Aug 20 '15 at 21:47
3

The getopts builtin parses options. You run it only once for all the options, then you process the operands (non-option arguments) that are left. getopts allows the caller to indifferently write e.g. moveToBin -iv file1 or moveToBin -i -v file1, and you can write moveToBin -- -file to handle file names that begin with a dash (anything after -- is interpreted as an operand).

getopts keeps track of how many arguments it's already processed through the OPTIND variable. When it's finished its job, OPTIND is the index of the first operand; since arguments are numbered from 1, this means that the first OPTIND-1 arguments were options.

As long as you're parsing options, you don't know yet the list of files to process. So remember the option by setting a variable, and query the variable later.

#!/bin/bash
confirm=
verbose=
while getopts 'iv' OPTLET; do
  case $OPTLET in
    i) confirm=y;;
    v) verbose=y;;
    \?) exit 3;;  # Invalid option; getopts already printed an error message
  esac
done
shift $((OPTIND-1))

for file in "$@"; do
  if [[ -n $confirm ]]; then
    read -p "want to delete $param ?" ans
    if [[ $ans != [Yy]* ]]; then
      continue       # the user said no, so skip this file
    fi
  fi
  … # do that moving stuff
  if [[ -n $verbose ]]; then
    echo "File $file deleted"
  fi
done

Note that getopts follows the traditional option parsing model, where anything after the first operand is a non-option. In other words, in moveToBin -i foo -v bar, there is the -i option and then three files foo, -v and bar. If you want to allow the GNU option parsing model where options can be mixed with operands, getopts isn't particularly helpful. Nor can bash's getopts builtin parse GNU long options (--verbose would be parsed like -v -e -r -b -o -s -e plus an error about - being unsupported).

  • I think it's very important to point out that getopts stops with the first non-option argument which is not intuitively understood when being used to commands like ´rm´ (and trying to replace them) , i.e. rm file1 file2 -i is possible with rm, while ./script file1 file2 -iv will fail and treat -iv as operand. Also the options cannot be expressed in long form (like in rm --force). I know you are aware, but OP surely is not. – Fiximan Aug 20 '15 at 22:50
0
#!/usr/bin/env bash

VERBOSE=n

while getopts "vC:" opt; do
  case $opt in
    v)
      VERBOSE=y
      ;;
    C)
      echo got $OPTARG
      ;;
  esac
done
shift $(($OPTIND - 1))

echo "soooo verbose is $VERBOSE"

for x in "$@"; do
  echo "still got: $x"
done

And then after the usual chmoddery and whatnot:

$ ./gopt hi there
soooo verbose is n
still got: hi
still got: there
$ ./gopt -v hi there
soooo verbose is y
still got: hi
still got: there
$ ./gopt -C cats -v hi there
got cats
soooo verbose is y
still got: hi
still got: there
$ 
  • please show the results for ./gopt hi there -v -C cats to point out the limitations of getopts - see my comment on Gilles' answer. – Fiximan Aug 20 '15 at 22:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.