如果我想自动"包裹";一个程序(为了给它添加一些环境变量(,一种解决方案是用类似myscript-wrapped
的方式重命名程序,并创建一个新文件myscript
,其中包含以下内容:
#/usr/bin/env bash
export PATH=/my/new/path
exec -a "$0" "myscript-wrapped" "$@"
-a "$0"
用于确保myscript-wrapped
的第一个自变量是myscript
而不是myscript-wrapped
,"$@"
用于转发自变量。
如果myscript-wrapped
是一个二进制文件,那么一切都很完美,即它认为第一个参数是myscript
。但是如果myscript-wrapped
是一个脚本(bash、python…(,比如:
#!/usr/bin/env bash
echo "my name is $0"
那么这就失败得很惨:我得到的是输出my name is myscript-wrapped
而不是my name is myscript
。我想发生这种情况是因为间接级别:myscript
调用bash
,然后bash
调用myscript-wrapped
。
避免这个问题的最佳方法是什么?如果脚本语言足够好,一种解决方案是编辑脚本myscript-wrapped
,并在一开始添加一行,如BASH_ARGV0="myscript"
,但这需要一个相当新的bash版本(至少5,我的系统上仍在运行版本4(。该解决方案的第二个问题是,它是非常特定于语言的,我们需要为任何语言编写不同的解决方案。有更好的主意吗?如果需要的话,我也很乐意用一些编译过的C来代替myscript
。
我的用例是,我想改进Nix包装器(Nix包管理器大量使用包装器来确保纯度/确定性(。
--编辑1-
我想我想做一些不可能的事情。我从https://man7.org/linux/man-pages/man2/execve.2.html("解释器脚本"部分(:
请注意,无法获取传递给execve((调用`的argv[0]
所以我想最好的做法是,当要包装的是bash脚本时,直接附加";"出口";一开始的东西。。。不是很便携,但我们再也找不到更好的了。
我想,如果一个人愿意依赖不同的GLIBC,那么可以直接修改GLIBC
,自动搜索二进制/脚本旁边的任何特别命名的文件,以填充环境变量。。。但这是一个相当大的变化,可能很难维持^^(在Nix的范围之外,这没有多大意义(。
--编辑2-
对于脚本,我终于为我的包装器问题找到了一个更简单的解决方案,它适用于脚本(二进制文件无论如何都不会遇到同样的问题,所以我想旧的解决方案对它们来说已经足够好了(。请参阅下面的答案。
我无法回答为什么它没有按预期工作。但是,如果使用-c选项,则可以向bash脚本提供$0,因此myscript
可以是:
#/usr/bin/env bash
PATH=$(dirname "$0"):$PATH
exec bash -c ". myscript-wrapped" "$0" "$@"
.
(又名source
(命令从PATH 中查找要源的文件
演示:
$ cat myscript-wrapped
#!/usr/bin/env bash
echo "my name is $0"
echo My args are:
printf " %sn" "$@"
$ ./myscript foo bar baz
my name is ./myscript
My args are:
foo
bar
baz
我终于为我的问题找到了一个不错的解决方案:(我仍然需要以不同的方式考虑脚本和二进制文件(:我没有将脚本外部化,而是通过定义一个自定义解释器来将包装器外部化,该解释器的角色是首先配置环境变量(该解释器的语言可以是任意的,我只使用bash(,然后调用原始解释器(可以是任何解释器:bash、python…(。这个解决方案很有趣,因为它不依赖于使用的脚本。它只有一个缺点:如果你想从不同的文件夹运行它,解释器路径必须是绝对的。
my_script.py
:
#!./my_python_wrapper
# Must be absolute if you don't plan to run it from the script folder.
import sys
import os
print("Here is my name {}, the variable MYENV is {}".format(sys.argv[0], os.environ["MYENV"]))
my_python_wrapper
:
#!/usr/bin/env bash
# Setup environment variables...
export MYENV="WIN :-)"
# Call the original wrapper, and forward all arguments.
exec /usr/bin/env python3 "$@"
演示:
$ ./my_script.py
Here is my name ./my_script.py, the variable MYENV is WIN :-)
如果您可以检测到myscript-wrapped
是一个shell,那么您就有了source
的方法。
使用名为myscript
的外壳包装器,如以下所示:
#!/bin/sh
. $*
myscript
将是外壳的myscript_shell
:
exec -a "$0" myscript "myscript-wrapped" "$@"
输出:
$ exec -a myscript ./myscript "./myscript-wrapped"
my name is /somewhere/myscript
myscript
将是二进制文件的myscript
:
exec -a "$0" "myscript-wrapped" "$@"