exec-a不适用于脚本:有什么方法可以轻松地更改脚本中的$0吗



如果我想自动"包裹";一个程序(为了给它添加一些环境变量(,一种解决方案是用类似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" "$@"

最新更新