从shell脚本中可移植地设置环境变量

时间:2017-01-09 23:53:16

标签: bash shell scripting zsh

我有这个shell脚本,我想修改它以确保它在Bash环境中正确运行。

这是脚本:

#!/bin/zsh

# Some constants. The first two will become env variables.
UPDATE_DNS_API_HOST="https://example.com"
UPDATE_DNS_API_URL="$UPDATE_DNS_API_HOST/my_end_point"
CURRENT_PUBLIC_IP=$(curl -s "$UPDATE_DNS_API_URL" | grep -o '".*"' | tr -d '"')

if [[ $PUBLIC_IP == $CURRENT_PUBLIC_IP ]]; then
  echo "Current IP: "$CURRENT_PUBLIC_IP" already set."
else
  response=$(curl -s -H "Content-Type: application/json" \
             --data "$CURRENT_PUBLIC_IP" "$UPDATE_DNS_API_URL")
  echo $response
  export PUBLIC_IP="$CURRENT_PUBLIC_IP"
fi

以下是我的问题:

  • 我应该将第一行更改为#!/bin/bash
  • 目前还不清楚变量何时需要引用以及何时不需要引用,特别是在条件语句中。你能指点我一些资源吗?
  • 我看过有关单支架与双支架的条件变化。我应该使用哪一个?
  • 运行脚本后,似乎没有设置$ PUBLIC_IP。我应该设置env变量吗?

欢迎任何其他反馈。

2 个答案:

答案 0 :(得分:1)

  • 如果您希望有人调用script.shbash而不是zsh下运行,那么您必须修复shebang。

  • 内部双引号:

    echo "Current IP: "$CURRENT_PUBLIC_IP" already set."
    

    是非正统的,不是个好主意。使用:

    echo "Current IP: $CURRENT_PUBLIC_IP already set."
    

    你可能想要双引号:

    echo "$response"
    

    否则,我认为你没问题。至于资源,您可以查看Bash manual,或使用shellcheck.com等设施,或咨询Bash FAQ

  • 如果您使用[[ … ]],您可能会写好。 shell语法的常规规则在[[ … ]]中暂停,并且让那些学习Bourne shell的人感到困惑,并且不会费心去学习Bash的那部分。

  • 您必须点(. script.sh)或源(source script.shexport PUBLIC_IP="$CURRENT_PUBLIC_IP"的脚本才能对调用shell产生任何影响。否则,它会设置运行脚本的shell环境,但不会影响调用shell。

    如果您确定要使用带有dot命令的脚本,那么您应该考虑在设置完成之前应该设置多少个变量。也许它们是否在它们开始之前被设置了。创建一个函数,除了要导出的变量之外,所有变量都声明为local,这样可以简化生活。当脚本作为单独的进程运行时,您不必担心这一点。

答案 1 :(得分:1)

这里需要记住的一件事是UNIX进程可以为自己和未来的孩子修改环境变量 - 他们的父母,没有父进程直接参与。

如果你的意图是在封闭shell中设置一个变量,一种相当常见的方法是在stdout上发出shell命令。这意味着任何不是 shell命令的东西都应该移动到stderr(无论如何这都是合适的做法,因为stderr被指定为适用于信息文本和状态内容)。

此版本确实需要bash,而不是/bin/sh,但是使用printf '%q'来确保它能够以eval生成变量名称 - 所有ksh派生的安全方式(ksh) ,bash,zsh)应该能够阅读。

#!/bin/bash
# note that while this runs with bash, ksh and zsh will also be able to eval its output
# ...POSIX sh too, when there aren't nonprintable characters causing $''-style quoting
# ...to be used.

# usage: emit_cmd varname ...
#
# emit code that defines a variable when evaluated on stdout
emit_cmd() {
  for varname; do
    printf 'export %q=%q; ' "$varname" "${!varname}"
  done
}

# Some constants. The first two will become env variables.
UPDATE_DNS_API_HOST="https://example.com"
UPDATE_DNS_API_URL="$UPDATE_DNS_API_HOST/my_end_point"

# print definitions of those variables to stdout
emit_cmd UPDATE_DNS_API_HOST UPDATE_DNS_API_URL

CURRENT_PUBLIC_IP=$(curl -s "$UPDATE_DNS_API_URL" | grep -o '".*"' | tr -d '"')

if [[ $PUBLIC_IP = $CURRENT_PUBLIC_IP ]]; then
  echo "Current IP: $CURRENT_PUBLIC_IP already set." >&2
else
  response=$(curl -s -H "Content-Type: application/json" \
             --data "$CURRENT_PUBLIC_IP" "$UPDATE_DNS_API_URL")
  echo "$response" >&2
  PUBLIC_IP="$CURRENT_PUBLIC_IP" emit_cmd PUBLIC_IP
fi

如果此脚本以名称ip-lookup保存,则可以使用以下命令将其定义的变量导入当前shell:

eval "$(ip-lookup)"

使用此约定可保持与需要修改环境变量的ssh-agent等现有UNIX工具的兼容性。

请注意,我保留了有关变量名称的现有约定,但如果有机会,则应切换到小写名称以符合relevant POSIX convention