【问题标题】:`exec -a` does not work for scripts: is there a ways to easily change $0 in scripts?`exec -a` 不适用于脚本:有没有办法在脚本中轻松更改 $0?
【发布时间】:2021-06-26 16:53:38
【问题描述】:

如果我想自动“包装”一个程序(以便向其中添加一些环境变量),一种解决方案是将程序重命名为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 版)。该解决方案的第二个问题是它非常特定于语言,我们需要为任何语言编写不同的解决方案。有更好的主意吗?如果需要,我也很乐意将 myscript 替换为一些已编译的 C。

我的用例是我想改进 Nix 包装器(Nix 包管理器使用大量包装器来确保纯度/确定性)。

-- 编辑 1--

我想我正在尝试做一些不可能的事情。我从https://man7.org/linux/man-pages/man2/execve.2.html 看到(“解释器脚本”部分):

请注意,无法获取传递给 execve() 调用的 argv[0] `

所以我想最好的办法是,当要包装的东西是 bash 脚本时,直接在开头附加“导出”的东西......不是很便携,但我们无法得到更好的东西。

我想如果有人愿意依赖不同的 GLIBC,可以直接修改 GLIBC 以自动搜索二进制/脚本旁边的任何特殊命名的文件,以填充环境变量.. . 但这是一个相当大的变化,可能很难维护^^(并且在 Nix 的范围之外没有多大意义)。

-- 编辑 2--

对于脚本,我终于为我的包装器问题找到了一个更简单的解决方案,它适用于脚本(二进制文件无论如何都不会遇到同样的问题,所以我猜旧的解决方案对它们来说已经足够好了)。请参阅下面的答案。

【问题讨论】:

    标签: python bash arguments command-line-arguments


    【解决方案1】:

    我终于为我的问题找到了一个不错的解决方案:(我仍然需要以不同的方式考虑脚本和二进制文件):我没有将脚本外部化,而是通过定义一个自定义解释器来外部化包装器,该解释器的作用是首先配置环境变量(这个解释器的语言可以是任意的,我只用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 :-)
    

    【讨论】:

    • 使用 env 作为调用者的好主意
    【解决方案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 "  %s\n" "$@"
    
    $ ./myscript foo bar baz
    my name is ./myscript
    My args are:
      foo
      bar
      baz
    

    【讨论】:

    • 感谢您的回答。可悲的是,它不能移植到任何脚本语言,但如果它是我们能得到的最好的......我会再等一会儿,看看我是否能找到更通用的方法来继续,但我认为这是不可能的。
    【解决方案3】:

    如果你能检测到myscript-wrapped 是一个shell,那么你就有了source 的方式。

    使用一个名为 myscriptshell-wrapper,如下所示:

    #!/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" "$@"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-31
      • 2016-03-18
      • 2018-06-19
      • 2012-03-22
      • 1970-01-01
      • 2020-07-31
      • 1970-01-01
      • 2015-09-05
      相关资源
      最近更新 更多