Your quoting is wrong. When you write $CMD
with no quotes, the value of $CMD
is broken up into “words” at each whitespace sequence¹ (the words can contain any non-whitespace character including punctuation), and then each word undergoes globbing (i.e. wildcard expansion). Note that quotes in the value of CMD
, in particular, are untouched: quotes have a meaning in the syntax of shell scripts but not in variable substitution. After this, the first word becomes the name of the command to execute, and subsequent words are the command's arguments.
In your example, assuming $KEYNAME
is somekeyname
and $HZID
is somehzid
, then the
command and arguments are:
./dnscurl.pl
--keyname
$KEYNAME
--
-X
POST
-H
"Content-Type:
text/xml;
charset=UTF-8"
--upload-file
/tmp/file.xml
https://route53.amazonaws.com/2010-10-01/hostedzone/somehzid/rrset
Note that text/xml;
appears as the first non-option argument; clearly the Perl script passes that argument down to curl
. The --
in the argument list is unrelated to your problem.
There's no way to stuff a command line into a variable. A (simple) variable is the wrong tool for that: it contains a string, but a command line is a list of strings (the command, and its arguments). You can stuff a command name and its argument into an array variable:
CMD=(./dnscurl.pl --keyname "$KEYNAME" --
-X POST -H "Content-Type: text/xml; charset=UTF-8"
--upload-file /tmp/file.xml
"https://route53.amazonaws.com/2010-10-01/hostedzone/$HZID/rrset")
RESULT=$("${CMD[@]}")
The strange-looking syntax "${CMD[@]}"
expands the array variable CMD
to the list of words in the array. The double quotes prevent expansion of words inside the array, and [@]
is needed for historical reasons to tell the shell that you want to expand an array.
Another way to remember a command line, or an arbitrary shell snippet, for later use, is a function.
cmd () {
./dnscurl.pl --keyname "$KEYNAME" -- \
-X POST -H "Content-Type: text/xml; charset=UTF-8" \
--upload-file /tmp/file.xml \
"https://route53.amazonaws.com/2010-10-01/hostedzone/$HZID/rrset"
}
RESULT=$(cmd)
¹ More precisely, according to the value of IFS
.
The regex below will extract the number of bytes, only the number:
contentlength=$(
LC_ALL=C sed '
/^[Cc]ontent-[Ll]ength:[[:blank:]]*0*\([0-9]\{1,\}\).*$/!d
s//\1/; q' headers
)
After the above change, the contentlength
variable will be only made of decimal digits (with leading 0s removes so the shell doesn't consider the number as octal), thus the 2 lines below will display the same result:
echo "$(($contentlength/9))"
echo "$((contentlength/9))"
Best Answer
For historical reasons, the exit status of a process is an 8-bit number. The number that you pass to
exit
is reduced modulo 256 (2⁸).Furthermore values 126 and above have a conventional meaning, indicating a failure to start the program (126 or 127) or a program that was killed by a signal (128 and above). So while you can return such values, you shouldn't unless you want to simulate these conditions.
The rule is to return 0 for success and some other value to indicate errors. This is a rule inasmuch as many tools interpret 0 as success: conditional commands such as
if
andwhile
, tools that abort on errors such as shell scripts underset -e
and makefiles, etc.Regarding errors, the most common conventions are to return 1 on any error, or to return 2 on any error. The thirdmost common convention is to return 1 on an expected failure (e.g. a search command that finds nothing) and 2 on an unexpected failure (e.g. the file to search in did not exist). If you need to map HTTP error codes into exit status, you have the range 1–125 to play in (with 200 success mapping to 0), there's no standard for that.