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
.
sh
(as in the sh
language specification) doesn't come with a line editor. Terminal drivers have a rudimentary line editor that allow for backspace and a few other keys to edit the entered line, but generally not arrow keys.
You can insert a default value into the terminal driver input buffer using the TIOCSTI
ioctl like:
printf 'Please enter the value: '
value=the-default
perl -MPOSIX -e 'require "sys/ioctl.ph"; tcflush 0,2;
ioctl(STDIN, &TIOCSTI, $_) or die "$!\n"
for split "", join " ", @ARGV' "$value"
IFS= read -r value
Upon the read
, the content of $value
(the-default
) will have been inserted as if typed.
Now, if you want a more advanced line editor like provided by libreadline
where you can use arrow keys, you can use things like rlwrap
(not a standard command though):
value=the-default
value=$(rlwrap -S 'Please enter the value: ' -P "$value" -o cat)
rlwrap
is typically used to add a readline
-like line editors to applications that don't have one. Above we're adding a line editor to cat
, and using it in one-shot mode (-o
), so that cat
returns after one line is entered (though you can still enter more than one line with Ctrl+V, Ctrl-J like in bash
.
If you're ready to use non-standard shells, zsh
or bash
have builtins for that using their own line editor.
In zsh
:
value=the-default
vared -p 'Please enter the value: ' value
In bash
:
value=the-default
IFS= read -re -i "$value" -p 'Please enter the value: ' value
Best Answer
This is a job for
expect
.Lets say your C program outputs something similar to the following:
You would write an expect script such as:
Then either
chmod +x
the script and run it, orexpect /path/to/script