0

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. ???
ACK_stoverflow
  • 3,148
  • 4
  • 24
  • 32
  • 1
    Consider a Bash array: `passthru_args=()`; `passthru_args+=( "$OPTARGS" )`; and `./er "${passthru_args[@]}"`. – Jonathan Leffler May 13 '14 at 21:14
  • You'd also use an array in `nerf`, of course: `er_args=( "-p" "-jkl" "-p" "-m" "-p" "$file_contents")` and `./herd -p "${er_args}[@]}"`. You should probably use `$(...)` in lieu of backticks in ``file_contents="`cat one_liner_file`"`` too. – Jonathan Leffler May 13 '14 at 21:31

1 Answers1

3

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).

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • Awesome, OPTIND was the solution! I made it configurable - either use the passthru flag -p as the last argument to herd, OR use the semicolon: `while [ -n "${!OPTIND}" ] && [ "${!OPTIND}" != ';' ]`. – ACK_stoverflow May 14 '14 at 15:49
  • @ACK_stoverflow. Nice! You can also compare `$OPTIND` to `$#` to allow empty arguments. – that other guy May 14 '14 at 15:52
  • I must be tired, because it took me WAY too long to figure this out, so I'll point it out for anyone else: make sure, in your getopts string, you DO NOT put a colon after 'p'. If you do, the next token after the -p flag is turned into an OPTARG, and is ignored when you're skipping through the OPIND indices. – ACK_stoverflow May 14 '14 at 21:19
  • but what oo you do if you want to pass something like `sshfs -o option="1 2"` – Fuseteam May 13 '21 at 03:03