/bin/sh 不当行为

/bin/sh 不当行为

我正在尝试使用一些简单的标志创建一个运行脚本/bin/sh

#!/bin/sh

set -eux

if [ "$DEBUG" = "true" ]; then
    debug="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=$SUSPEND,address=$DEBUG_PORT"
else
    debug=''
fi

serv="--server.port=${SERVER_PORT}"
prop="--spring.profiles.active=${PROFILES}"

all="${serv:-} ${prop:-}"

java "${debug:-}"-jar /opt/someJar.jar "${all:-}"

现在我发现了一些我无法解释的问题,也无法在谷歌中找到它为什么会这样。

  1. 请注意,"${debug:-}"-jar没有空间。当我放置一个空格时,应用程序会中断并显示“java 无法找到或加载主类”。当我删除空间时,它会按预期工作。
  2. 我有两个变量servprop。如果我将这两个 as 放在"${serv:-}" "${prop:-}"java 命令的末尾,那么这两个参数将在应用程序中单独传递。但是当我把"${all:-}"then 从 java 中的异常中增加时,我可以看到这两个是连接在一起的。
Caused by: java.lang.NumberFormatException: For input string: "1234--spring.profiles.active=some-profile"

但因为它set -eux打印

+ java -jar /opt/someJar.jar --server.port=1234 --spring.profiles.active=some-profile

这就是我真正想要的。

如果我因任何原因没有提供足够的信息,我可以提供更多信息。我试图实现的目标是理解它为什么会这样,以及如何将其用于/bin/sh像这样的一些简单脚本。

答案1

Stéphane 提到$@在普通 shell 中用作数组。这是一种方法:

#!/bin/sh

set -eux
set --      # clear cmdline params

if [ "$DEBUG" = "true" ]; then
    set -- "-Xdebug" "-Xrunjdwp:transport=dt_socket,server=y,suspend=$SUSPEND,address=$DEBUG_PORT"
fi

# specify the jar file
set -- "$@" -jar /opt/someJar.jar

# add the port and profile
set -- "$@" "--server.port=${SERVER_PORT}" 
set -- "$@" "--spring.profiles.active=${PROFILES}"

# and run it
java "$@"

答案2

sh没有数组("$@"位置参数数组除外)。

在:

java "${debug:-}"-jar /opt/someJar.jar "${all:-}"

java仅传递 3 个参数(除了java它本身)。

当您打算传递两个参数时,最后一个将包含类似--server.port=1234 --spring.profiles.active=some-profile嵌入空格的内容:--server.port=1234--spring.profiles.active=some-profile

A标量(相对于大批) 变量与$allin一样sh,只能包含一个值。

相反,您可以将"$@"数组用作格伦已经表明(我也喜欢混合$IFS并且noglob非常混乱)或者你可以告诉 shell分裂通过将变量设置为分隔符来扩展变量的内容$IFS,并将参数扩展保留为不带引号的(在禁用通配符之后,因为默认情况下也会在不带引号的参数扩展时进行通配符)。

对于$IFS,您需要选择一个不能出现在参数中的字符,并且如果您想保留空参数,该字符不能是 SPC、TAB 或 NL(所有 3 个字符恰好都在 的默认值中$IFS)。

然而,在您的情况下,您似乎希望一个空$serv实例在为空时导致没有相应的参数$serv(而不是一个空参数),因此空白$IFS可能是一个更好的主意。 NL 可能是一个不错的选择,因为它不太可能在您的论点中找到:

serv="--server.port=${SERVER_PORT}"
prop="--spring.profiles.active=${PROFILES}"
IFS="
" # newline character only
all="${serv:-}${IFS}${prop:-}"
set -o noglob # disable glob

java ... /opt/someJar.jar $all

您需要为 做类似的事情$debug

set -o noglob如果您可以保证所有参数都不包含通配符、大括号或反斜杠,则可以跳过。

请注意,xtrace您的 shell 的输出有点误导。它通过原始输出并用 SPC 字符分隔来表示传递给命令的参数列表。这意味着我们无法区分参数中的 SPC 字符和用于xtrace分隔参数的输出字符。

其他一些 shell 更有用,因为当存在此类歧义时,它们会在参数周围输出引号。

set -x; printf "<%s>\n" foo "bar baz"比较几个不同 shell 解释时的输出:

$ dash -c 'set -x; printf "<%s>\n" foo "bar baz"'
+ printf <%s>\n foo bar baz
<foo>
<bar baz>
$ bosh -c 'set -x; printf "<%s>\n" foo "bar baz"'
+ printf <%s>\n foo bar baz
<foo>
<bar baz>
$ bash -c 'set -x; printf "<%s>\n" foo "bar baz"'
+ printf '<%s>\n' foo 'bar baz'
<foo>
<bar baz>
$ ksh -c 'set -x; printf "<%s>\n" foo "bar baz"'
+ printf '<%s>\n' foo 'bar baz'
<foo>
<bar baz>

您的sh似乎属于dash/bosh类别。看看他们的+ printf <%s>\n foo bar bazxtrace 输出如何让您认为printf正在传递 3 个foo, bar,baz参数,而实际上它正在传递两个参数:foobar baz

相关内容