RebelLion
RebelLion

Reputation: 33

How to pass command with its arguments as a single argument to script?

I have a script I use for building my kernel, and in it I have a function I want to use to catch errors in my build commands, but I can't figure out how to pass multiple arguments as one to get it working properly. The syntax is as follows:

run_build() {
cmd=$($1)
status=${PIPESTATUS[0]}
$*
if ($status != 0); then
    echo "$cmd failed!"
    exit 1
else
    echo "$cmd succeeded."
fi
}

The problem is, when I try to pass a make command with its arguments, the make command is being executed with no arguments. I tried to use the "$*" syntax to call all arguments but it doesn't seem to work. An example command would be

make O=out -j8 zImage

I need the next 3 arguments to be included when calling the line from the script. Anyone have ideas?

Upvotes: 1

Views: 1422

Answers (3)

Paul Hodges
Paul Hodges

Reputation: 15246

Not fond of this setup for a few obvious reasons and few less obvious.

For the record, c.f. this page on why not to do this.
Also worthy of note, in case you shift to eval, which idealogically is kind of what this function is doing... please read this too.

...but for a simplistic solution -
Arguments come in as an array. If you are running the build, then make your function run the build, with quoted arguments.

run_build() {
  if make "$@"  # QUOTED, *explicit* call to make
  then echo "'make $@' succeeded."
  else echo "'make $@' failed."
       return 1 # exit in function only sets return - be explicit
  fi
}

run_build O=out -j8 zImage # run_build as a make synonym

Don't try to build do-anything functions.
That way lies madness.

If you must use other commands than make, then validate and verify.

run_build() {
  local cmd=$1
  shift
  case "$cmd" in
  make|or|some|other|specifically|ok|word)
    if "$cmd" "$@" # QUOTED!!!
    then echo "'$cmd $@' succeeded." 
    else echo "'$cmd $@' failed."
         return 1 
    fi 
  ;;
  *) echo "Invalid argument: '$cmd' not recognized."
     return 1
  ;;
  esac
}

If you aren't at least doing that, what's the purpose of the function? I mean, you are passing in all the parts of the command anyway, and not doing much with them beside executing them. Why not just...you know, execute them, instead of bothering with a function? But even if you keep it to it's simplest, quote, quote, quote.

run_build() { "$@" && echo "'$@' succeeded" || {
                      echo "'$@'failed; return 1; }; }

Upvotes: 1

tripleee
tripleee

Reputation: 189317

The proper solution to this is probably something like

if "$@"; then
    echo "$@ succeeded" >&2
else
    rc=$?
    echo "$@ failed" >&2
fi

but this still seems like you are replacing perfectly good built-in functionality with something worse.

Upvotes: 1

glenn jackman
glenn jackman

Reputation: 246744

First point, you don't want to pass the entire command as a single argument: I'm trying to put a command in a variable, but the complex cases always fail!

This is the main error: cmd=$($1) -- the $() syntax invokes Command Substitution that executes the first argument. Just use cmd=$1 to store the first parameter into the "cmd" variable.

Additional errors:

  • if ($status != 0); then -- that syntax is incorrect: use if ((status != 0)); then for proper arithmetic evaluation.
  • $* -- to execute the positional parameters correctly, use "$@" (with the quotes) -- that form will maintain any arguments that contain whitespace as intended.

Upvotes: 0

Related Questions