Issue
I have written a helper script (Z shell (executable zsh
) dialect) that installs Python's Poetry package manager. A weird problem occurs where the same shell command is interpreted differently, when run (a) in a shell script vs (b) directly in Z shell. An excerpt of the script is this:
#!/bin/zsh
set -x
INSTALL_SPECIFIC_VERSION="--version 1.6.1"
# Why does this not work?
echo "curl -sSL URL | python3 - $INSTALL_SPECIFIC_VERSION"
# will print:
# curl -sSL URL | python3 - --version 1.6.1
curl -sSL URL | python3 - "$INSTALL_SPECIFIC_VERSION"
# The fix I use which works but I don't understand why, is:
# curl -sSL URL | python3 - $(printf "%s" "$INSTALL_SPECIFIC_VERSION")
set +x
Here is the result when the shell script is executed:
➜ /usr/bin/zsh test_script.zsh
+test_script.zsh:3> INSTALL_SPECIFIC_VERSION='--version 1.6.1'
+test_script.zsh:5> echo 'curl -sSL URL | python3 - --version 1.6.1'
curl -sSL URL | python3 - --version 1.6.1
+test_script.zsh:8> curl -sSL URL
+test_script.zsh:8> python3 - '--version 1.6.1'
usage: - [-h] [-p] [--version VERSION] [-f] [-y] [--uninstall] [--path PATH] [--git GIT]
-: error: unrecognized arguments: --version 1.6.1
+test_script.zsh:9> set +x
The argument --version 1.6.1
can not be interpreted by remotely called shell script.
When I call the printed (echo) cmd in Z shell it works fine. See:
➜ /usr/bin/zsh -c "curl -sSL URL | python3 - --version 1.6.1"
Retrieving Poetry metadata
The latest version (1.6.1) is already installed.
My current fix in the shell file is this:
# The fix I use which works but I don't understand why, is:
curl -sSL URL | python3 - $(printf "%s" "$INSTALL_SPECIFIC_VERSION")
How come the variable's value is passed/interpreted differently. What am I missing here?
Z shell version: 5.8.1 (x86_64-ubuntu-linux-gnu)
Solution
You cannot (sensibly nor sanely) store multiple words (arguments, commends, etc) in a single variable. What you can do however, is to expand a parameter with an alternative value containing a single word:
${parameter:+word}
Use Alternative Value. If parameter is unset or null, null shall be substituted; otherwise, the expansion of word shall be substituted.
In your case:
#!/bin/sh -x
# you don't need zsh for this, plain sh works just fine
INSTALL_SPECIFIC_VERSION="1.6.1"
curl -sSL URL | python3 - ${INSTALL_SPECIFIC_VERSION:+--version "$INSTALL_SPECIFIC_VERSION"}
This will expand to nothing if the parameter is not set to a value and expand to two words --version
and 1.6.1
if the parameter contains a value.
Alternatively, use plain old if
:
#!/bin/sh -x
INSTALL_SPECIFIC_VERSION="1.6.1"
if test "$INSTALL_SPECIFIC_VERSION"; then
curl -sSL URL | python3 - --version "$INSTALL_SPECIFIC_VERSION"
else
curl -sSL URL | python3 -
fi
Here's why you always want to quote your parameter expansions: Security implications of forgetting to quote a variable in bash/POSIX shells. Running arbitrary commands from a remote server is also security-critical and should be avoided if possible (but sometimes there's no way around it).
Resources:
Answered By - knittl Answer Checked By - Clifford M. (WPSolving Volunteer)