Search code examples
shposix

Subset a string in POSIX shell


I have a variable set in the following format:

var1="word1 word2 word3"

Is it possible to subset/delete one of the space-delimited word portably? What I want to archive is something like this: when --ignore option is supplied with the following argument

$ cmd --ignore word1 # case 1
$ cmd --ignore "word1 word2" # case2

I want the var1 changes to have only the following value

"word2 word3" # case1
"word3" #case2

If there is no way to achieve above described, is there a way to improve the efficiency of the following for loop? (The $var1 is in a for loop so my alternative thought to achieve similar was having following code)

# while loop to get argument from options
# argument of `--ignore` is assigned to `$sh_ignore`
for i in $var1
do
  # check $i in $sh_ignore instead of other way around 
  # to avoid unmatch when $sh_ignore has more than 1 word
  if ! echo "$sh_ignore" | grep "$i";
  then
    # normal actions
  else
    # skipped
  fi
done

-------Update-------

After looking around and reading the comment by @chepner I now temporarily using following code (and am looking for improvement):

sh_ignore=''
while :; do
  case
    # some other option handling
    --ignore)
      if [ "$2" ]; then
          sh_ignore=$2
          shift
        else
          # defined `die` as print err msg + exit 1
          die 'ERROR: "--ignore" requires a non-empty option argument.'
        fi
        ;;
    # handling if no arg is supplied to --ignore
    # handling -- and unknown opt
  esac
  shift
done

if [ -n "$sh_ignore" ]; then
  for d in $sh_ignore
  do
    var1="$(echo "$var1" | sed -e "s,$d,,")"
  done
fi

# for loop with trimmed $var1 as downstream
for i in $var1
do
  # normal actions
done

Solution

  • One method might be:

    var1=$(echo "$var1" |
           tr ' ' '\n' |
           grep -Fxv -e "$(echo "$sh_ignore" | tr ' ' '\n')" |
           tr '\n' ' ')
    

    Note: this will leave a trailing blank, which can be trimmed off via var1=${var1% }