如果我在命令行运行它,它将使提交在指定的日期:
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
答案2
背景
有几件事需要解释。
每个进程都有自己的环境。环境包含零个或多个环境变量。每个变量都是与名称(也是字符串)关联的值(字符串)。shell 实例(如 Bash)是一个进程,因此有一些与之关联的环境。
在 shell 中,您可以定义、访问和修改 shell 变量。与环境变量一样,这些变量也是带有名称的字符串。给定变量不一定位于 shell 的环境中。当 shell 启动时,环境变量会作为 shell 变量出现在 shell 中,但您可以创建不在环境中的变量。shell 环境中的变量称为“导出变量”。
POSIX shell ( sh
)、所有 (几乎) POSIX 兼容的 shell(例如 Bash)以及一些故意偏离 POSIX 的 shell(例如 Zsh)使用相同的接口来访问环境变量和其他 shell 变量。如果要获取来自环境的变量的值或不在环境中的foo
shell 变量的值,请使用。请注意,这是一个设计决定,可以设计成不同的形式(例如foo
$foo
在 Python 中你可以通过以下方式访问环境变量os.environ
这与访问“正常”变量(如)不同x
。
如果想在其环境中运行something
,name=value
有以下几种方法:
如果
name
是已经导出后,将其设置为所需的值,然后执行something
:name=value something
进程将从其父进程(即 shell)继承环境,因此
name=value
将处于进程的环境中。注意name=value
将保留在 shell 的环境中。无论是否
name
在 shell 环境中,您都可以将其导出,因此从现在开始name
肯定在环境中。显然,您应该将变量设置为所需的值。所有这一切可以通过两个步骤完成:name=value export name
或只需一步:
export name=value
然后执行
something
。像以前一样,name=value
将保留在shell的环境中。
name
注意:即使不在 shell 环境中,下面的方法也应该有效,因此如果您想真正测试它们,那么您应该使用取消导出变量export -n name
(该命令不可移植,它在 Bash 中有效)。
你可以告诉 shell 在环境中执行
something
,name=value
而不影响 shell 的环境:name=value something
(请注意,这与不同
name=value; something
。后者等同于方法 1;仅当name
已导出时它才会起作用。)可以将多个变量放入环境中
something
:name=value foo=bar baz=… something
您可以运行另一个程序,该程序将从 shell 继承环境变量并
something
使用定制的环境运行。正确的程序是env
:env name=value something
与上一种方法一样,此方法不会改变 shell 的环境。这
name=value
只是 的参数之一env
,一个字符串,在解释它之前与变量无关env
。该方法可能看起来像上一种方法的一个无用且更长的替代方案,但它有其优点:env
something
让您轻松运行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 环境中的存在会使它们被导出,并且您的分配将用所需的值覆盖来自初始环境的值。git
script.sh
这并没有发生,相关变量不在环境中。
要修复脚本,您应该export
在调用之前设置相关变量。在或 中git
执行此操作并不重要,因为当您从一个脚本切换到另一个脚本时,解释其内容的 shell 进程是相同的。vars.sh
script.sh
source
旁注:如果您source vars.sh
还从另一个脚本export
中获取变量vars.sh
,那么在另一个脚本中它们将被导出;不导出vars.sh
允许您独立(或不独立)获取和另一个脚本中的export
变量。script.sh
另一种解决方法是使用name=value something
方法。由于THE_TIME
作为 shell 变量存在,因此您的原始行也可以工作。如果没有任何export
,git 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
。
export
ing 更容易,但如果您想要一个特定子进程(在许多子进程中)的环境中的一些变量,那么替代修复很有用。
笔记
引用。在我看来这样更好总是用双引号引用变量替换,即使你知道什么时候不引用是安全的和当不安全的时候. 不能引用的情况很少见(有些情况是因为不知道正确的方法而出现的);例子),因此始终引用可以让您处于安全的一边,而不必每次都费力地考虑在这种特殊情况下是否可以省略引号。
就您的问题而言:
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
到文件名中。