使用不同的 /bin/sh 分隔环境

使用不同的 /bin/sh 分隔环境

我有一堆 shell 脚本,它们错误地认为/bin/sh等同于/bin/bash.例如,他们有#!/bin/shshebang,但使用source命令而不是.(点)。

我运行 Ubuntu 16,其中/bin/sh链接到dash,因此不支持 bash-isms。

我需要定期运行脚本。另外,我有时需要从原作者处更新它们,而原作者并不想修复这个特定的错误。我想避免修复所有这些文件(其中有很多文件,它们不是我的,更新后我将丢失所有更改)。另外,我想避免对系统进行全局更改,因为它可能会破坏其他脚本。

有没有办法以某种方式创建一个/bin/sh指向 bash 的(临时或非临时)环境,用于这些脚本,同时不接触全局系统/bin/sh

答案1

我想挂载命名空间或类似的东西可以用来安排不同的进程/用户对是什么有不同的想法/bin/sh

但这听起来很黑客,也可以算作“对系统进行永久性更改”。仅进行一行修复可能会更容易。使该修复成为更新过程的一部分,并发布错误报告和有关错误的 hashbang 上游的补丁。

对于 GNU sed,应该这样做来修复它们:

sed -i -e '1s,^#! */bin/sh,#!/bin/bash,' /all/the/scripts/*

答案2

如果/bin/sh->/bin/dash在你的系统上是一个动态链接的可执行文件,就像在我的系统上一样(你可以使用 file(1) 检查它),你可以使用 hackLD_PRELOAD来实现。

它的工作原理如下:加载一个小型动态库覆盖LD_PRELOADglibc __libc_start_main(调用可执行文件函数的函数main()),然后使用相同的参数argv[0] == /bin/sh执行 exec /bin/bash(除了argv[0]);否则它会调用原始内容__libc_start_main,就好像什么也没发生一样。

$ cat sh_is_bash.c
#define _GNU_SOURCE     /* for RTLD_NEXT */
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#include <err.h>
int __libc_start_main(
        int (*main)(int,char**,char**), int ac, char **av,
        int (*init)(int,char**,char**), void (*fini)(void),
        void (*rtld_fini)(void), void *stack_end)
{
        typeof(__libc_start_main) *real_lsm;
        if(ac > 0 && !strcmp(av[0], "/bin/sh")){
                av[0] = "/bin/bash";
                execv(av[0], av);
                err(1, "execv %s", av[0]);
        }else if(real_lsm = dlsym(RTLD_NEXT, "__libc_start_main"))
                return real_lsm(main, ac, av, init, fini, rtld_fini, stack_end);
        else
                errx(1, "BUG: dlsym: %s", dlerror());
}
$ cc -fPIC -shared -Wall -W -Wno-parentheses sh_is_bash.c -o sh_is_bash.so -ldl
$ LD_PRELOAD=`pwd`/sh_is_bash.so program ...

任何带有 shebang 的脚本都将在环境变量包含 的绝对路径时执行,而不是#! /bin/sh执行。/bin/bash/bin/shLD_PRELOADsh_is_bash.so

这很丑陋,但它不需要对系统或脚本进行任何硬更改,它很容易部署和管理,并且不需要任何特殊权限。

答案3

您可以轻松修复它们,而不会破坏您的系统!

find . -name '*.sh' -type f -exec sed -i '1s|^#! */bin/sh|#!/bin/bash|' {} +

答案4

在您的情况下,包含非 POSIX 扩展的脚本应该具有相关的正确#!标头:

#!/bin/bash

所以我认为没有办法编辑所有不正确的脚本。

顺便说一句:如果你真的确定,你可以创建一个 bash 的临时链接并重命名它:

cd /bin
ln -s bash nsh
mv nsh sh

由于mv以原子方式工作,这将确保始终有一个工作/bin/sh

因此,重命名时当前运行的脚本和其他 shell 将继续工作,并且重命名后,这将调用 bash 而不是 dash。

但是,如果您的系统以允许编辑脚本的方式运行,我宁愿只编辑脚本。

如果您替换/bin/sh为 的链接bash,请不要忘记修复此问题,以便在完成后再次/bin/sh成为 的链接。dash

如果有问题的脚本是二进制数据包的一部分,请不要忘记向上游提交针对该问题的错误报告。

相关内容