Question

Script nerf calls script herd, which calls script er. nerf uses a flag on herd that explicitly takes arguments needing to be passed to er.

This was not a problem before nerf existed - when herd was just called from the command line, we could single-quote the arguments to the -p flag, and they would never be interpreted by herd's getopts, but instead they would be interpreted by er's getopts.

But now we have generated values in the flags that eventually need to go to er, so I need to expand the variable $file_contents in nerf, but not let them be interpreted by getopts until they get to er.

Any of these three scripts can be modified.

$ cat nerf
#!/bin/bash
file_contents="`cat one_liner_file`"
er_args="-jkl -m $file_contents"
./herd -p "$er_args"              # <-- the problem


$ cat herd
#!/bin/bash
passthru_args=""
while getopts "p:a:b:cde" opt
do
  case $opt in
    p) passthru_args="$OPTARGS" ;;
    ...
  esac
done
./er "$passthru_args"


$ cat er
#!/bin/bash
while getopts "jklm:" opt
do
  case $opt in
    ...
  esac
done

If I use single quotes on the marked line above, I get the literal string "$er_args" passed through. Using double quotes, the flags are directly interpreted by herd. Using single inside double quotes, the flags aren't interpreted by ANY getopts.

I'm thinking there's no elegant solution here, but please let me know if I'm wrong. The only solutions I can think of are crappy:

  1. Expose all of er's flags explicitly through herd.
  2. Remove the er call from herd and place it directly into nerf.
  3. ???
Was it helpful?

Solution

What many tools do is when passed -p "-jkl -m something", they split up the string using pseudo-shell syntax. This is a bad idea because it makes space and quote handling unpredictable.

Instead, the better way is to have a way to pass individual words to the command. This is what find -exec does -- all arguments after -exec and up until + or ; are passed literally as separate arguments.

Here's a simple example of a herd with the same semantics:

#!/bin/bash
passthru_args=()
while getopts "pa:b:cde" opt
do
  case $opt in
    p)
      while [[ ${!OPTIND} != ';' ]]
      do
        passthru_args+=("${!OPTIND}")
        let OPTIND++
      done
      let OPTIND++
    ;;
    *) echo "herd: $opt is $OPTARG"
    ;;
  esac
done
./er "${passthru_args[@]}"

You can now run ./herd -p -jkl -m "some stuff" \; -a foo

This will run ./er -jkl -m "some stuff" safely without any space issues (but you'll have a hard time nesting multiple calls that use ; as an argument terminator).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top