1

I have a wrapper shell script that needs to add some library and include paths before calling a compiler.

#!/bin/sh
LDIRS="-L/opt/lib"
LDFGS="-llibA -llibB"

exec /opt/bin/xxx $@ $LDIRS $LDFGS

this works fine for compiling a simple test case

compiler -o test test.c

It falls apart if another program would like to call my compiler and pass in include directories like this

compiler -o in_file.xx -I/xxx -I/xxx

How could I generalize this to get the expected behavior of appending those includes to LDFGS?

6
  • Unquoted $@ has a number of undesirable behaviors. Always use "$@". Similarly, better to use arrays rather than strings when you want to pass a list: ldirs=( -L/opt/lib ); ldfgs=( -llibA -llibB ); ... "${ldirs[@]}" "${ldflgs[@]}" -- that way you aren't subject to string-splitting and glob expansion. See BashFAQ #50 re: why using unquoted expansions for commands (or subsets of commands) is harmful. Commented Oct 5, 2018 at 20:00
  • Beyond that, to have an answerable question we really need a minimal reproducible example for a specific bug: A standalone program (that doesn't require any other tools to be installed to work), expected output (which we can see ourselves by copy-and-pasting it to run), and actual output (which a correct answer will be able to attain, thus making answers concretely testable). Commented Oct 5, 2018 at 20:03
  • ...btw, adding -x to your shebang, as in #!/bin/sh -x, will give you concrete logging, which is definitely something that would be helpful here. Commented Oct 5, 2018 at 20:04
  • Consider yourprogram -DVERSION="foo 1.2.3" as a concrete example of a place where $@ will burn you: -DVERSION=foo and 1.2.3 will be treated as two separate arguments. "$@" has no such issue. Commented Oct 5, 2018 at 20:05
  • 1
    Glad to see you are human @CharlesDuffy :) Commented Oct 6, 2018 at 4:46

1 Answer 1

1

I take it that the order of the LDIRS and LDFGS is the relevant issue; the additional things the user provides are supposed to end up in the LDFGS (i. e. need to be given after all the LDIRS to /opt/bin/xxx) but don't with your current implementation. So I take it that the -I/xxx arguments are to be added to the LDFGS. If I misunderstood your issue, please state more clearly what you wanted. Otherwise read on.

You have two options to solve an issue like this. Either you implement some kind of intelligence which understands the given options and sorts them properly into the lists where they belong. An approach to this could be like this:

#!/bin/sh
LDIRS="-L/opt/lib"
LDFGS="-llibA -llibB"
PRE=""

for argument in "$@"
do
  case "$argument" in
    -I*)
      LDFGS="$LDFGS $argument"
      ;;
    # other patterns could go here as well
    *)  # else case
      PRE="$PRE $argument"
      ;;
  esac
done

exec /opt/bin/xxx $PRE $LDIRS $LDFGS

As @charles-duffy already pointed out, using a bash or similar which supports proper lists would be way more robust in case any of your arguments contain spaces. If you are sure this won't happen, you should be fine for now. But your code maintainer will hate you for such things when they run into trouble because of this. So here's a less readable version in sh which should take care of this:

#!/bin/sh
LDIRS="-L/opt/lib"
LDFGS="-llibA -llibB"
PRE=""

for argument in "$@"
do
  case "$argument" in
    -I*)
      LDFGS=`printf "%s %q" "$LDFGS" "$argument"`
      ;;
    # other patterns could go here as well
    *)  # else case
      PRE=`printf "%s %q" "$PRE" "$argument"`
      ;;
  esac
done

eval "exec /opt/bin/xxx $PRE $LDIRS $LDFGS"

(Whenever eval is used, a disclaimer needs to be added: eval has it's dangers, some of them security-relevant, so please learn more about it before applying it wildly. The current unmodified case should be fine, though.)

If you think that you have no way of knowing all patterns which are supposed to go into LDFGS, LDIRS, and PRE, you need to hand this over to the user. Unfortunately, then the user needs to know more and pass this information. The calls then will need to look differently.

One way would be this:

#!/bin/sh
LDIRS="-L/opt/lib"
LDFGS="-llibA -llibB"
PRE=""

while [ $# -gt 1 ]
do
  case "$1" in
    -p)  # go into PRE
      PRE="$PRE $2"
      shift 2
      ;;
    -i)  # go into LDIRS
      LDIRS="$LDIRS $2"
      shift 2
      ;;
    -f)  # go into LDFGS
      LDFGS="$LDFGS $2"
      shift 2
      ;;
    *)
      echo "Not understood: $1"
      exit 1
      ;;
  esac
done

eval "exec /opt/bin/xxx $PRE $LDIRS $LDFGS"

Now the call needs to look like this:

compiler -p "-o in_file.xx" -f "-I/xxx -I/xxx"

And again, if you have spaces issues, you should consider using proper array solutions or at least the ugly workaround I proposed above.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.