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

xzlaal3s  于 2022-11-16  发布在  Shell
关注(0)|答案(2)|浏览(224)

我想修改这个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变量?

欢迎您提供任何其他反馈。

qlfbtfca

qlfbtfca1#

这里要记住的一个相关的事情是,UNIX进程可以为它们自己和它们启动的未来子进程修改环境变量-- * 而不是 * 它们的父进程,父进程不直接参与。
如果你想在封闭的shell中设置一个变量,一个相当常见的方法是在stdout上发出shell命令,这意味着任何 * 不是 * shell命令的东西都应该被移到stderr(无论如何,这是一个合适的做法,因为stderr被指定为适合于信息文本和状态内容)。
/bin/sh相反,这个版本确实需要bash,但是使用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)"

使用此约定可保持与现有UNIX工具(如ssh-agent)的兼容性,这些工具需要修改环境变量。
请注意,我保留了现有的变量名约定,但如果有机会,应该改用小写名称以符合relevant POSIX convention

h9vpoimq

h9vpoimq2#

  • 如果你想让调用script.sh的人在bash而不是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的这一部分。
  • 您必须为export PUBLIC_IP="$CURRENT_PUBLIC_IP"的脚本添加dot(. script.sh)或source(source script.sh),这样才能对调用shell产生任何影响。否则,它会设置运行脚本的shell的环境,但不会影响调用shell。

如果你决定用dot命令来使用脚本,那么你应该考虑在它完成之前有多少变量应该被取消设置。也许在它开始之前是否有任何变量被设置。创建一个函数,把所有变量声明为局部变量,除了你想导出的变量,这会让事情变得更容易。当脚本作为一个单独的进程运行时,你不必担心这个问题。

相关问题