开发者

Possible spacing issue in a bash script. Command will not run in script but will when copied from output

I've gone around and around on the quoting stuff on http://tldp.org for bash and googled until I am blue in the face. I've also tried every obvious quoting scheme for this issue, and yet nothing works.

The problem seems to be that a space inside of a quoted argument in the command run at the end of the script is being interpreted as a separator instead of as a quoted space.

Behold, here's my script (I know full well I'm a noob so comments on my style and/or uneccessary syntax is cool with me, I'll learn):

#!/bin/bash

date=`date`
args="$@"
MSEND_HOME=/home/patrol/Impact           #Path to the Impact Directory
integrationName=Introscope               #Name of the integration
persistEnabled=1                         #1 for Yes, 0 for No
persist=""
bufDir=$MSEND_HOME/tmp/$integrationName  #DO NOT CHANGE
cellName=linuxtest                       #Cell name to forward events to

loggingEnabled=1                         #1 for Yes, 0 for No
logFile=$MSEND_HOME/log/$integrationName.$cellName.log


die () {
  if [ $loggingEnabled -eq 1 ]
  then
    echo >>$logFile "$@"
  fi
  exit 1
}

[ "$#" -ge 1 ] || die "$date - At least 1 argument required, $# provided" "$@"

# This is where you would parse out your arguments and form the following
# slots as a minimum for sending an event.

class=$2
msg=\"$3\"

# Parse the first argument and assign the correct syntax
if [[ $1 == "INFORMATIONAL" ]]
then
  severity=INFO
elif [[ $1 == "WARN" ]]
then
  severity=WARNING
elif [[ $1 == "CRIT开发者_运维问答" ]]
then
  severity=CRITICAL
else
  severity=INFO
fi

#Additional slots can be set, parse them all in this variable;
#e.g., additionalSlots="slot1=value1;slot2=value2;slot3=\"value 3\""
additionalSlots=""


cmd="$MSEND_HOME/bin/msend"
cmd="$cmd -q"
cmd="$cmd -l $MSEND_HOME"

if [ $persistEnabled -eq 1 ]
then
  cmd="$cmd -j $bufDir"
fi

cmd="$cmd -n $cellName"
cmd="$cmd -a $class"
cmd="$cmd -m $msg"
cmd="$cmd -r $severity"

if [ $additionalSlots ]
then
  cmd="$cmd -b $additionalSlots"
fi

$cmd || die "$date - msend exited with error $? | Original arguments: $args | Command: $cmd"
#echo "msend exited with error $? | Original arguments: $args | Command: $cmd"

The script is executed like this:

./sendEvent.sh "CRIT" "EVENT" "Test Event"

The error I get from the msend executable is that the arguments are wrong, but I'm logging the command line in it's entirety to a file and when I run that logged command in the shell interactively, it works.

Here's the log output:

Tue Oct 4 20:31:29 CDT 2011 - msend exited with error 27 | Original arguments: CRIT EVENT Test Event | Command: /home/patrol/Impact/bin/msend -q -l /home/patrol/Impact -j /home/patrol/Impact/tmp/Introscope -n linuxtest -a EVENT -m "Test Event" -r CRITICAL

So if I paste /home/patrol/Impact/bin/msend -q -l /home/patrol/Impact -j /home/patrol/Impact/tmp/Introscope -n linuxtest -a EVENT -m "Test Event" -r CRITICAL and run it, it works.

If I run the script like ./sendEvent.sh "CRIT" "EVENT" "TestEvent" it works. But I need that argument to allow spaces.

I'm on the track that it's an $IFS issue or something... maybe a difference between the interactive shell and the script environment.

I'd appreciate any insight from smarter people than me!

tl;dr - My command doesn't work when run from within a script, but does when the logged command syntax is used in an interactive shell.


Short answer: see BashFAQ #50.

Long answer: When bash parses a line, it parses quote marks before doing variable substitution; as a result, when you put quotes inside a variable, they don't do what you'd expect. You're actually passing an argument list including '-m' '"Test' 'Event"' '-r' -- those double-quotes aren't around the arguments, they're in the arguments.

In this case, the best solution is to build the command in an array rather than a string. Also, get in the habbit of putting double-quotes around variables (e.g. filenames) when you use them, to prevent confusion if they contain spaces. With those changes (and a few other tweaks), here's my version of your script:

#!/bin/bash

date="$(date)"  # Backquotes are confusing, use $() instead
args=("$@")   # Save the args in an array rather than mushing them together in a string
MSEND_HOME=/home/patrol/Impact           #Path to the Impact Directory
MSEND_HOME="$HOME/tmp"           #Path to the Impact Directory
integrationName=Introscope               #Name of the integration
persistEnabled=1                         #1 for Yes, 0 for No
persist=""
bufDir="$MSEND_HOME/tmp/$integrationName"  #DO NOT CHANGE
cellName=linuxtest                       #Cell name to forward events to

loggingEnabled=1                         #1 for Yes, 0 for No
logFile="$MSEND_HOME/log/$integrationName.$cellName.log"


die () {
  if [ $loggingEnabled -eq 1 ]
  then
    echo >>"$logFile" "$@"
  fi
  exit 1
}

[ "$#" -ge 1 ] || die "$date - At least 1 argument required, $# provided" "$@"

# This is where you would parse out your arguments and form the following
# slots as a minimum for sending an event.

class="$2"   # Quotes not strictly needed here, but a good habbit
msg="$3"

# Parse the first argument and assign the correct syntax
if [[ "$1" == "INFORMATIONAL" ]]
then
  severity=INFO
elif [[ "$1" == "WARN" ]]
then
  severity=WARNING
elif [[ "$1" == "CRIT" ]]
then
  severity=CRITICAL
else
  severity=INFO
fi

#Additional slots can be set, parse them all in this array;
#e.g., additionalSlots="slot1=value1;slot2=value2;slot3=value 3"  # Don't embed quotes
additionalSlots=""


cmd=("$MSEND_HOME/bin/msend")   # Build the command as an array, not a string
cmd+=(-q)   # Could equivalently use cmd=("${cmd[@]}" -q), but this is simpler
cmd+=(-l "$MSEND_HOME")

if [ $persistEnabled -eq 1 ]
then
  cmd+=(-j "$bufDir")
fi

cmd+=(-n "$cellName")
cmd+=(-a "$class")  # Possible bug: $2 and @3 aren't required, but they're getting added unconditionally
cmd+=(-m "$msg")    # These should probably be conditional, like additionalSlots
cmd+=(-r "$severity")

if [ -n "$additionalSlots" ]
then
  cmd+=(-b "$additionalSlots")
fi

"${cmd[@]}" || die "$date - msend exited with error $? | Original arguments:$(printf " %q" "${args[@]}") | Command:$(printf " %q" "${cmd[@]}")"
#echo "msend exited with error $? | Original arguments:$(printf " %q" "${args[@]}") | Command:$(printf " %q" "${cmd[@]}")"


I think the arg goes wrong with this assignment: cmd="$cmd -m $msg".

Change it to cmd="$cmd -m \"$msg\"".


Okay, I don't see the exact problem immediately, but I can tell you what it is; this hint should help.

Remember that the shell quoting mechanism only interprets a string once. As a result, if you're not careful, what you thought was "foo" "a" "b" is in fact "foo a b" -- that is, all one token, not three.

Run the script with bash -x which will show you at each step what the shell is actually seeing.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜