如何取消导出变量而不丢失其值?

如何取消导出变量而不丢失其值?

假设我导出了一个变量:

foo=bar
export foo

现在,我想取消导出它。也就是说,如果我这样做了,sh -c 'echo "$foo"'我就不应该得到bar.根本foo不应该出现在sh -c的环境中。 sh -c仅仅是一个例子,一种显示变量存在的简单方法。该命令可以是任何东西 - 它的行为可能仅受其环境中变量的存在的影响。

我可以:

  1. unset变量,然后丢失它
  2. env使用每个命令删除它:env -u foo sh -c 'echo "$foo"'
    • 如果您想继续使用当前 shell 一段时间,这是不切实际的。

理想情况下,我希望保留变量的值,但根本不让它显示在子进程中,甚至不显示为空变量。

我想我可以这样做:

otherfoo="$foo"; unset foo; foo="$otherfoo"; unset otherfoo

otherfoo如果 已经存在,则存在被踩踏的风险。

这是唯一的方法吗?有没有标准的方法?

答案1

没有标准方法。

您可以通过使用函数来避免使用临时变量。以下函数负责保持未设置的变量未设置和空变量为空。但是,它不支持某些 shell 中的功能,例如只读或类型化变量。

unexport () {
  while [ "$#" -ne 0 ]; do
    eval "set -- \"\${$1}\" \"\${$1+set}\" \"\$@\""
    if [ -n "$2" ]; then
      unset "$3"
      eval "$3=\$1"
    fi
    shift; shift; shift
  done
}
unexport foo bar

在 ksh、bash 和 zsh 中,您可以使用 .unexport 取消导出变量typeset +x foo。这保留了特殊属性,例如类型,因此最好使用它。我认为所有具有typeset内置功能的 shell 都有typeset +x.

case $(LC_ALL=C type typeset 2>&1) in
  typeset\ *\ builtin) unexport () { typeset +x -- "$@"; };;
  *) unexport () { … };; # code above
esac

答案2

编辑:仅用于bash,正如评论中指出的:

从每个给定名称中删除属性-nexport选项。export(看help export。)

所以为了bash你想要的命令是:export -n foo

答案3

我编写了一个类似的 POSIX 函数,但这不会带来任意代码执行的风险:

unexport()
    while case ${1##[0-9]*} in                   ### rule out leading numerics
          (*[!_[:alnum:]]*|"")                   ### filter out bad|empty names
          set "" ${1+"bad name: '$1'"}           ### prep bad name error
          return ${2+${1:?"$2"}}                 ### fail w/ above err or return 
          esac
    do    eval  set '"$'"{$1+$1}"'" "$'"$1"'" "$'@\" ###  $1 = (  $1+ ? $1 : "" )
          eval  "${1:+unset $1;$1=\$2;} shift 3"     ### $$1 = ( $1:+ ? $2 : -- )
    done

它还将处理您愿意提供的尽可能多的参数。如果参数是一个有效名称,但尚未设置,则会默默地忽略它。如果参数是一个错误的名称,它会写入 stderr 并根据需要停止,但仍会处理其命令行上无效名称之前的任何有效名称。

我想到了另一个办法。我更喜欢它。

unexport()
        while   unset OPTARG; OPTIND=1           ### always work w/ $1
                case  ${1##[0-9]*}    in         ### same old same old
                (*[!_[:alnum:]]*|"")             ### goodname && $# > 0 || break
                    ${1+"getopts"} : "$1"        ### $# ? getopts : ":"
                    return                       ### getopts errored or ":" didnt
                esac
        do      eval   getopts :s: '"$1" -"${'"$1+s}-\$$1\""
                eval   unset  "$1;  ${OPTARG+$1=\${OPTARG}#-}"
                shift
        done

好吧,这两者都使用了很多相同的技术。基本上,如果 shell var 未设置,则对它的引用将不会通过+参数扩展进行扩展。但如果它被设置 - 无论它的值如何 - 参数扩展如下:${parameter+word}将扩展到word- 而不是变量的值。因此 shell 变量自检并自替换成功。

他们还可以自我失败。在顶部函数中,如果发现错误的名称,我会进入$1$2保留null,因为如果所有参数都已处理并且循环结束,$1我要做的下一件事是成功,或者如果参数无效,shell 将return展开其中$2$1:?杀死脚本化 shell 并在写入时将中断返回到交互式 shellword到标准错误。

在第二个中getopts做作业。它不会指定一个错误的名称 - 而是 write 它将向 stderr 写出一条标准错误消息。更重要的是,它将 arg 的值保存在$OPTARG 如果参数首先是一个集合变量的名称。因此,完成所有操作后,getopts需要将evalsetOPTARG扩展为适当的赋值。

相关内容