sudo: source: command not found

我一直在更新bash的默认配置文件,并从我所遵循的教程中了解到,我可以通过使用以下命令重新加载新的配置文件和环境设置:
source /etc/bash.bashrc

唯一的问题是 - 新的环境变量只对当前用户可用,并且在使用sudo时被忽略。只有当我关闭终端会话并重新加入后,它们才对sudo可用。
当我尝试使用:
sudo source /etc/bash.bashrc

我遇到了一个错误:
sudo: source: command not found

有没有一种简单的方法可以在不关闭终端并重新启动的情况下加载新的bash配置文件设置给sudo使用?
-- 最初,我使用了一些安装脚本来引用这些变量。我发现当我直接调用这些脚本时,它们可以访问这些变量(尽管后来在创建目录时会出现权限问题,因为我需要以root身份运行),但是使用sudo调用安装脚本则无法访问这些变量。
我通过以下简单命令进行了测试以证明这一点:
echo $ENV_VARIABLE
sudo echo $ENV_VARIABLE

第一个会输出变量的值,但第二个不会输出任何内容。

你是怎么尝试使用sudo的变量的?请注意,如果你使用"sudo command $variable"这样的方式,它会替换你的shell中的变量,而不是sudo的环境中的变量。 - João Pinto
9个回答

问题是source是一个bash内置命令(不是一个程序,比如lsgrep)。我认为一种方法是以root身份登录,然后执行source命令。
sudo -s
source /etc/bash.bashrc

3你说得对,问题在于source是一个shell内置命令。sudo su这种说法有点奇怪,最好直接使用sudo -s,这是sudo自己的方式来表示“以该用户身份启动一个shell”。你的一行版本不会起作用,因为其中的每个命令都是由主用户的shell在单独的子进程中运行的。 - poolie
1对的。此外,BASH在登录时会读取/etc/bashrc文件。因此,你可以使用'su'命令加上'-', '-l'或'--login'选项来获取该用户的环境:使用'sudo su -'切换为root用户,或者使用'su - $username'切换到另一个用户。 - user8290
1“一行代码”的例子不起作用,因为su会启动一个新的shell,而“source”只有在它终止后才运行。第一个例子仅在根shell内使用第二行时才起作用。 - loevborg
sudo -ssudo su并没有什么区别。无论哪种方式都不会产生任何影响。 - loevborg
1sudo -s的作用类似于启动一个shell,但对我来说,在只需要一个命令时,堆叠两个"成为另一个用户"的命令似乎不够优雅。 - poolie

问题不在于source是一个内置命令。实际上,它是导致你出现command not found错误的原因,但这并不意味着如果它是一个外部命令就能正常工作。
实际的问题在于环境变量的工作方式。它们的工作方式如下: 每当启动一个新进程时,如果没有发生任何事情,它会继承其父进程的环境。因此,使用子shell(例如在bash实例中键入bash)并查看env的输出应该与其父进程产生类似的结果。
然而,由于sudo的工作方式(如其手册所述),sudo试图剥离用户的环境,并为替代用户创建一个“默认”环境,以便运行的命令被视为调用者本身运行的命令(这是预期的行为)。因此,以sudo nautilus的形式运行nautilus应该在/root文件夹中打开一个文件夹,而不是/home/yourusername
所以:
做类似于sudo source script.sh然后sudo command这样的操作,即使它能够运行,也不能成功地将任何变量设置给后续的sudo command
为了传递环境变量,你可以告诉sudo保留环境(通过-E开关;并在sudoers文件中具有适当的权限),和/或者将其设置为命令的方式如sudo VAR1=VALUE1 VAR2=VALUE2 command

使用bash的进程替代,您可以这样做:
source <(sudo cat /etc/bash.bashrc)

1这将如何帮助使用新设置启动根shell,这正是OP想要做的? - muru
3原帖实际上是在询问如何从shell中重新加载新配置文件,这需要使用sudo访问该配置文件。以上提供了一种导入配置文件的方法,同时避免了之前提到的sudo:source:命令未找到问题。 - TomDotTom
唯一的问题是,新的环境变量只对我的当前用户可用,并且在我使用sudo时被忽略。 - muru
1这是我最喜欢的答案。它非常有效。 - MountainX
对我来说很有效。非常好的解决方案。我需要以另一个用户的身份获取 .bash_profile 文件,但不能直接使用 su 切换到该用户。以下方法可行:source <(sudo -u theuser cat /home/theuser/.bash_profile)。谢谢。 - Matt

正如马科斯所说, 你在这里的主要问题是source是一个仅影响其运行的shell进程的内置命令。

简单的解决方案是以root身份启动一个新的shell,当它启动时bash会自动读取/etc/bash.bashrc。只需输入以下命令即可:

sudo bash

关闭和重新打开终端不应该改变任何东西。默认情况下,sudo会剥离环境变量。要禁用此功能,请在sudo后面添加-E选项。

错误发生的原因是您尝试从命令行调用的二进制文件只是当前用户的PATH变量的一部分,而不是root用户的PATH的一部分。
您可以通过查找要访问的二进制文件的路径来验证这一点。在我的情况下,我尝试调用"bettercap-ng"。所以我运行了以下命令:
$ which bettercap-ng
/home/user/work/bin/bettercap`

我检查了这个位置是否是我的根用户的PATH的一部分。
$ sudo env | grep ^PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin

所以sudo找不到我试图从命令行调用的二进制文件。因此返回错误命令未找到。
您可以通过以下方式指示sudo在调用二进制文件时使用当前用户的PATH。
sudo -E env "PATH=$PATH" [command] [arguments]

实际上,你可以用它来创建一个别名。
alias mysudo='sudo -E env "PATH=$PATH"'

也可以将别名本身命名为sudo,替换原始的sudo。


一些UNIX shell不支持source,而是支持.所以试试这个。
. /etc/bash.bashrc

希望它能够正常运行。

2这在这里行不通。它仍然是一个内置的shell命令,即使它能够工作,它也会建立一个环境,在命令完成后立即消失。 - Gordon Davisson

源代码在您的本地账户上无需使用sudo命令运行。

这并没有回答问题。一旦你拥有足够的声望,你就可以评论任何帖子;相反,提供不需要提问者澄清的答案。- 来自审核 - TheOdd

它不起作用是因为source是一个内置命令,而不是一个程序。我编写了一个bash脚本来强制在内置命令上使用sudo:
#!/bin/bash

function forceSudo()
{
    command="${@}"
    file="${@: -1}"
    if ! $command 2>/dev/null
    then
        permission=$(stat -c '%a' $file)
        sudo chmod o+rx $file
        result=$command 2>/dev/null
        sudo chmod $permission $file
        if ! $result
        then
            echo $result
        fi
    fi
}

将文件保存为forceSudo,并将其保存在脚本位置,可能是~/.local/bin。为了避免在使用函数之前必须先源化文件,请将alias forceSudo='unalias forceSudo && . forceSudo && forceSudo "$@"'添加到~/.bashrc中。
现在您可以使用forceSudo source /etc/bash.bashrc