在 Bash 脚本中设置环境变量不起作用?

在 Bash 脚本中设置环境变量不起作用?

如果我在命令行运行它,它将使提交在指定的日期:

THE_TIME='2022-01-01T22:50:12 -0700' GIT_AUTHOR_DATE=$THE_TIME GIT_COMMITTER_DATE=$THE_TIME git commit -am 'commit'

但是,当我将其放入file.sh脚本(chmod 0755)并像 一样运行它时./file.sh,它似乎没有拾取这些变量。 有什么想法可以让命令git commit在 bash 脚本中拾取这些临时环境变量吗?

我尝试了两种方法,但都没有用。它不使用指定日期,而是使用当前日期。

首先,我尝试像上面那样做,但是在脚本中:

update() {
  local name="$1"
  echo "$name"
  cd "./$name"
  git add .
  THE_TIME='2022-01-01T22:50:12 -0700' GIT_AUTHOR_DATE=$THE_TIME GIT_COMMITTER_DATE=$THE_TIME git commit -am 'commit'
  git push
  cd ..
}

update my-repo
update my-second-repo
# ... apply to many repos

其次,我尝试将它们放入单独的文件中并用 加载它们source ./vars.sh,如下所示:

# vars.sh
THE_TIME='2022-01-01T22:50:12 -0700'
GIT_AUTHOR_DATE=$THE_TIME
GIT_COMMITTER_DATE=$THE_TIME
# script.sh
source ./vars.sh

update() {
  local name="$1"
  echo "$name"
  cd "./$name"
  git add .
  git commit -am 'commit'
  git push
  cd ..
}

update my-repo
update my-second-repo
# ... apply to many repos

我做错了什么?为什么 git 没有获取环境变量?我使用的是最新的 13.0.1 Mac OS。

答案1

为了设置环境变量(与本地脚本变量不同),使用 出口 命令如下:

export GIT_AUTHOR_DATE=$THE_TIME

更多信息请参阅:

答案2

背景

有几件事需要解释。

每个进程都有自己的环境。环境包含零个或多个环境变量。每个变量都是与名称(也是字符串)关联的值(字符串)。shell 实例(如 Bash)是一个进程,因此有一些与之关联的环境。

在 shell 中,您可以定义、访问和修改 shell 变量。与环境变量一样,这些变量也是带有名称的字符串。给定变量不一定位于 shell 的环境中。当 shell 启动时,环境变量会作为 shell 变量出现在 shell 中,但您可以创建不在环境中的变量。shell 环境中的变量称为“导出变量”。

POSIX shell ( sh)、所有 (几乎) POSIX 兼容的 shell(例如 Bash)以及一些故意偏离 POSIX 的 shell(例如 Zsh)使用相同的接口来访问环境变量和其他 shell 变量。如果要获取来自环境的变量的值或不在环境中的fooshell 变量的值,请使用。请注意,这是一个设计决定,可以设计成不同的形式(例如foo$foo在 Python 中你可以通过以下方式访问环境变量os.environ这与访问“正常”变量(如)不同x

如果想在其环境中运行somethingname=value有以下几种方法:

  1. 如果name已经导出后,将其设置为所需的值,然后执行something

    name=value
    something
    

    进程将从其父进程(即 shell)继承环境,因此name=value将处于进程的环境中。注意name=value将保留在 shell 的环境中。

  2. 无论是否name在 shell 环境中,您都可以将其导出,因此从现在开始name肯定在环境中。显然,您应该将变量设置为所需的值。所有这一切可以通过两个步骤完成:

    name=value
    export name
    

    或只需一步:

    export name=value
    

    然后执行something。像以前一样,name=value将保留在shell的环境中。

name注意:即使不在 shell 环境中,下面的方法也应该有效,因此如果您想真正测试它们,那么您应该使用取消导出变量export -n name(该命令不可移植,它在 Bash 中有效)。

  1. 你可以告诉 shell 在环境中执行somethingname=value而不影响 shell 的环境:

    name=value something
    

    (请注意,这与不同name=value; something。后者等同于方法 1;仅当name已导出时它才会起作用。)

    可以将多个变量放入环境中something

    name=value foo=bar baz=… something
    
  2. 您可以运行另一个程序,该程序将从 shell 继承环境变量并something使用定制的环境运行。正确的程序是env

    env name=value something
    

    与上一种方法一样,此方法不会改变 shell 的环境。这name=value只是 的参数之一env,一个字符串,在解释它之前与变量无关env。该方法可能看起来像上一种方法的一个无用且更长的替代方案,但它有其优点:

    • envsomething让您轻松运行name 已删除来自环境:

       env -u name something
      

      name当位于 shell 环境中,而你希望它停留在 环境中时,它很有用something。仅在 shell 中执行此操作是可能的,但有点麻烦。

    • env允许您使用对环境变量有效但对 shell 变量无效的名称。例如:

       env b-b=1 something
      

      由于env不带参数会打印其自己的环境,因此您可以通过运行来测试该方法是否真的有效env b-b=1 env;您将在输出中看到b-b=1。 上一个方法 ( b-b=1 env) 不适用于此名称。

技术说明:修改导出的变量、取消导出或导出时,shell 没有义务实际上改变它的环境。只要它表现得仿佛其环境已发生改变。因此,像“之后export name=value name在环境中”这样的陈述可能正确也可能不正确。“name被导出”是一个更好、更严格的术语;它意味着事情就像name在 shell 的环境中一样工作,即使实际上并非如此。


你的具体情况

对您有用的命令

THE_TIME='2022-01-01T22:50:12 -0700' GIT_AUTHOR_DATE=$THE_TIME GIT_COMMITTER_DATE=$THE_TIME git commit -am 'commit'

使用name=value something方法。重要的事实是name=value不会影响当前 shell;您的THE_TIME=…没有影响 shell。在您的命令中$THE_TIME,稍后的行被扩展为名为的 shell 变量(导出或未导出)的值THE_TIME。显然,这个变量当时存在,并且它的值在上下文中是合理的git,因此命令有效,您认为它是好的代码。问题是变量肯定不是来自这一行。您的THE_TIME=…只影响了git其环境中看到的内容(以及可能的子代,如果有的话)。

在脚本中,相同的命令不起作用,因为$THE_TIME扩展为其他内容;很可能是空字符串,因为THE_TIME它既不是从环境中启动的,也没有在脚本中声明。

如果你这样做了:

THE_TIME='2022-01-01T22:50:12 -0700'; GIT_AUTHOR_DATE=$THE_TIME GIT_COMMITTER_DATE=$THE_TIME git commit -am 'commit'
#                note the semicolon ^

然后 shell 会THE_TIME=…;首先解释,并设置名为 的自己的变量(无论是否导出)THE_TIME。该行的其余部分将在稍后解释,因此 的两个实例都$THE_TIME将按预期展开。THE_TIME但不会进入 的环境git(除非它之前已被导出)。

在您的vars.sh+尝试中,仅当变量已被导出时,script.sh变量才会进入环境,即,如果您在环境中使用同名变量运行。它们在解释脚本的 shell 环境中的存在会使它们被导出,并且您的分配将用所需的值覆盖来自初始环境的值。gitscript.sh

这并没有发生,相关变量不在环境中。

要修复脚本,您应该export在调用之前设置相关变量。在或 中git执行此操作并不重要,因为当您从一个脚本切换到另一个脚本时,解释其内容的 shell 进程是相同的。vars.shscript.shsource

旁注:如果您source vars.sh还从另一个脚本export中获取变量vars.sh,那么在另一个脚本中它们将被导出;不导出vars.sh允许您独立(或不独立)获取和另一个脚本中的export变量。script.sh

另一种解决方法是使用name=value something方法。由于THE_TIME作为 shell 变量存在,因此您的原始行也可以工作。如果没有任何exportgit commit -am 'commit'您可以这样做:

GIT_AUTHOR_DATE="$THE_TIME" GIT_COMMITTER_DATE="$THE_TIME" git commit -am 'commit'

如果您还想THE_TIME进入 的环境,git则应将其添加THE_TIME="$THE_TIME"到行的前面。单独来看,它看起来像是无操作,就像将变量分配给其自身一样,但实际上THE_TIME="$THE_TIME" something它将 shell 变量(无论是否导出)复制到 的环境something

exporting 更容易,但如果您想要一个特定子进程(在许多子进程中)的环境中的一些变量,那么替代修复很有用。


笔记

  • 引用。在我看来这样更好总是用双引号引用变量替换,即使你知道什么时候不引用是安全的当不安全的时候. 不能引用的情况很少见(有些情况是因为不知道正确的方法而出现的);例子),因此始终引用可以让您处于安全的一边,而不必每次都费力地考虑在这种特殊情况下是否可以省略引号。

    就您的问题而言:

    • GIT_AUTHOR_DATE=$THE_TIME(作为完整命令)无需在 周围添加双引号,也是安全的$THE_TIME。 该GIT_AUTHOR_DATE=部分不得用引号引起来。
    • export GIT_AUTHOR_DATE=$THE_TIME在 Bash 中,没有双引号是安全的$THE_TIME,但其他 shell 在解析此内容时可能会拆分 + 不带引号$THE_TIME。您可以引用或不引用GIT_AUTHOR_DATE=,这是一个安全字符串。任何有效的 shell 变量名称(加上=)都是安全字符串。
    • GIT_AUTHOR_DATE=$THE_TIME something在 Bash 中,不使用双引号也是安全的$THE_TIME,但其他 shell 在解析此内容时可能会拆分 + 不带引号$THE_TIME。此GIT_AUTHOR_DATE=部分不得加引号。
    • env GIT_AUTHOR_DATE=$THE_TIME something不是在包括 Bash在内的许多 shell 中,不使用双引号是安全的$THE_TIME。你可以引用 也可以不引用GIT_AUTHOR_DATE=,这是一个安全字符串。任何有效的 shell 变量名称(加上=)都是安全字符串,但由于env你可以使用其他名称创建环境变量(例如*),一般来说您可能需要引用整个name=value部分。
  • 命名。请TEST.sh参阅我的另一个答案. 使用正确的舍邦(看没有shebang会发生什么) 并停止将其添加.sh到文件名中。

相关内容