如果我在一行的开头输入“sudo”,它是否适用于其余命令?

如果我在一行的开头输入“sudo”,它是否适用于其余命令?

如果我sudo在 bash 中的一行的开头输入,它是否适用于其余命令?

换句话说,是这样的:

sudo foo | foo2 | foo3

相当于这个:

sudo foo | sudo foo2 | sudo foo3

答案1

作为一种说服自己sudo只使用提供的第一个命令运行的方法,并且第一个管道作为原始用户 ID 运行之后的所有其他内容,您可以使用这个无用的命令链来查看它。

例子#1

$ sudo whoami > file1 | whoami > file2 | whoami > file3

然后,当您查看cat这些文件时,您将看到以下用户名:

$ cat file{1..3}
root
saml
saml

例子#2

但是,如果您运行子 shell:

$ sudo sh -c 'whoami > file4 | whoami > file5 | whoami > file6'

然后,当您查看cat这些文件时,您将看到以下用户名:

$ cat file{4..6}
root
root
root

例子#3

您的评论sudo foo1 | sudo foo2 ...永远不会起作用,因为 的输出sudo foo1将被馈送到sudo foo2.使用我的whoami示例表明该命令链没有执行任何操作。

$ sudo whoami | sudo whoami | sudo whoami
root

第一个运行了,但第二个和第三个没有执行任何操作,因为它们没有能力接受输入。相反,我认为你的意思是写这样的东西:

$ sudo whoami;sudo whoami;sudo whoami
root
root
root

这相当于在 3 个不同的命令提示符下运行 3 次。或这个:

$ sudo whoami && sudo whoami && sudo whoami
root
root
root

但永远不要做最后一项。它属于下一节。

调用 sudo 的晦涩方法

这些不是我最好的工作,但您可能会看到我使用 执行多个命令的其他方式sudo。我在这里展示它们只是为了不让别人必然他们!

方法#1

$ echo "echo 1 > /proc/sys/vm/drop_caches" | sudo sh

怎么运行的?

由于 sudo,双引号内的 echo 程序以 root 身份运行,但将 echo 的输出重定向到仅 root 文件的 shell 仍在以您的身份运行。您当前的 shell 在启动之前执行重定向sudo

方式#2

$ sudo tee /proc/sys/vm/drop_caches <<<1

怎么运行的?

该方法运行tee以 root 身份运行程序并从 a 获取输入这里的字符串sudo它在调用命令之前运行tee

方式#3

# this way
$ sudo -s -- 'whoami'

# or this way
sudo -s -- sh -c 'whoami;whoami'

怎么运行的?

这些可能看起来不同,但实际上他们在做同样的事情。使用-s开关时,sudo将运行单个命令。我永远不知道是否有办法摆脱它。像这些都行不通。

# this
$ sudo -s -- 'whoami;whoami'

# or this
$ sudo -s -- 'whoami\;whoami'

但在查看手册页时,该-s开关表示它将向用户文件条目中定义的 shell 传递单个命令/etc/passwd。因此,我们使用第二种形式的技巧,主要是传递 shell,另一个 shell ( sh -c),我们在其中“后门”要运行的命令字符串。

还有更多,但我就到此为止。这些只是为了向您展示如果您理解事物可以做什么,但不一定只是将垃圾链接在一起,因为您可以,您应该尝试将代码编译指示保持在有意义的逻辑级别,以便其他人能够理解和支持他们在未来。

答案2

不,在您的示例中仅foo由 执行sudo。如果您想以升级的权限运行所有命令,您可以生成一个 shell:

sudo sh -c 'foo | foo2 | foo3'

答案3

不,但是为什么呢?

不,sudo不适用于管道的其余部分。这个另一个答案给出了一些例子,但它没有解释这一切背后的机制。

最重要的是sudo一个单独的可执行文件,与您运行它的任何 shell 无关。有问题的命令是:

sudo foo | foo2 | foo3

对于 shell 来说,这与cat foo | foo2 | foo3. shell 识别由三个部分组成的管道,并在sudo(或cat) 启动之前设置管道。 shell 不知道这foo最终将是一个命令。因为 shellfoo只是一个参数,它应该传递给sudo.

在上述情况下,以下两种情况都不会发生,但即使sudofoo|foo2|作为foo3参数,或者将其foo | foo2 | foo3作为一个参数,那么整个命令无论如何都不会按您的预期工作,因为|属于 shell 语法,并且sudo两者都不是默认情况下,shell 的工作也不运行 shell。


sudo使用;运行多个命令两种方法

你需要一个 shell 来解释|。这导致了两种基本方法:

  1. sudo foo | sudo foo2 | sudo foo3主壳体处理管道;
  2. sudo sh -c 'foo | foo2 | foo3'一个新的高架外壳处理管道。

它与&&和/或||替代 或 与类似|

请注意,在案例 2 中,主 shell 启动sudo, whilefoo | foo2 | foo3只是参数之一,可以是任何内容的字符串;然后稍后sudo开始并作为它的参数sh传递。foo | foo2 | foo3只有内部sh字符串才被解释为 shell 代码。

我写的sh -c,但你可以使用另一个shell。如果您需要 plain 不支持的功能,这尤其有意义sh


做出明智的选择

一般来说,我更喜欢带有附加 shell 的方法(方法 2)。这些是我的理由:

  • 只有一个sudo,它最多会要求提供一次凭据。在 的情况下sudo … | sudo …,根据配置(中sudoers),每个sudo可能会单独询问。该工具足够智能,因此使用同一终端的多个实例不会互相干扰,并且会按顺序询问,即使 shell 并行运行它们也是如此。如果情况更糟,sudo … && sudo …因为通常第二个sudo可能(有条件地)在数小时后开始,所以即使sudo配置为不过于频繁地询问您的密码,第二个也sudo可能会询问,并且您可能无法预测何时会发生这种情况。

  • 如果涉及重定向,那么您将不需要欺骗与tee(也不sudo cat … | …代替输入重定向)。让提升的 shell 处理重定向以最自然的方式解决问题。这假设您确实想以 root 身份打开一些文件。

另一方面,方法 2 也有缺点。在某些情况下,您可能需要选择方法 1。需要考虑的事项:

  • sudo some_shell -c …运行一个新的高架some_shell。可能存在错误some_shell,或者您传递给它的 shell 代码中可能存在错误;错误可能会导致 shell 执行意外操作。提升进程做一些意想不到的事情是一件非常糟糕的事情。因此,您可能想要提升foofoo2foo3,但仅此而已。

  • 传递给的 Shell 代码sh -c必须是单个参数。几乎在任何情况下(当然在您的情况下)这都需要引用或转义空格和其他字符。额外的引用/转义级别增加了复杂性。一般来说,您需要知道如何正确引用/转义已经引用或转义的内容。

    潜在有用的资源:如何在 Bash 中方便地使用单引号或转义整个命令行?

  • 由 启动的 shellsudo无法访问主 shell 的变量。导出变量可能有帮助,也可能没有帮助;sudo可以配置为将它们从环境中删除。将主 shell 扩展的变量嵌入到另一个 shell 的代码中是错误的嵌入{},除非您知道该值对此是安全的。我的意思是一般来说这是错误的:

    sudo sh -c "foo \"$var\" | foo2"

    有多种方法可以安全地传递变量。我只列出它们(可能还有其他):

    var="$var" sudo -E sh -c 'foo "$var" | foo2'   # may or may not work depending on sudoers and security policy
    sudo var="$var" sh -c 'foo "$var" | foo2'      # may or may not work depending on sudoers and security policy
    sudo env var="$var" sh -c 'foo "$var" | foo2'
    sudo sh -c "foo ${var@Q} | foo2"               # if the main shell is Bash
    sudo sh -c 'foo "$1" | foo2' sh "$var"
    

    第 1 号方法是:

    sudo foo "$var" | sudo foo2
    

    这既简单又安全。

  • 假设您对退出状态感兴趣foo和/或foo2sudo sh -c 'foo | foo2 | foo3'不会给您。即使您使用在运行管道后存储此信息的 shell(例如带有其 的 Bash $PIPESTATUS[@]),将信息从运行的内 shell 可靠地传递sudo到外部主 shell 也是一项不简单的任务,并且需要一些繁琐的代码(例如内壳和外壳分别)。

    同样,方法 1 很简单:

    sudo foo | sudo foo2 | sudo foo3
    

    每个sudo(只要它成功完成其工作)将中继相应foo*命令的退出状态。现在您所需要的只是主 shell 能够告诉您管道中每个命令的退出状态($PIPESTATUS[@]如果主 shell 是 Bash)。

  • 如果sudoers允许您运行有限数量的命令,sudo那么它很可能不允许sh(以及任何其他不受限制的 shell 或类似 shell 的实用程序,因为否则您无论如何都可以运行任何命令sudo)。如果foofoo2foo3允许使用sudo,但 shell 不允许,则方法 1 是您唯一的选择。


更广阔的前景

还有其他命令的行为类似于sudonohup、、、、...niceioniceunshare

甚至echo是这样的。你没想到echo

echo foo | foo2 | foo3

|和 其余部分作为其论据。从外壳中取出foo,就是这样,几乎没有人感到惊讶。

在任何(?) shell 中echo都是内置的,即 shell 的一部分。 “Builtin”意味着 shell 不运行外部echo可执行文件,它自己处理 echo 的工作;但内置的行为就像外部echo不过,有一些涉及信号的微妙之处)。

至少有一个命令在某些情况下表现不同,它是time.它是一个单独的可执行文件,当这样调用时,它的行为就像sudo在您的问题的上下文中一样。例如这个:

command time -p sleep 2 | sleep 4

在空闲操作系统中应该实时报告 2 秒(time打印到 stderr,因此尽管有管道,您仍会看到报告)。然而在 Bash 中time关键词这改变了 Bash 解析后续内容的方式。它是故意这样的,因此您可以测量整个管道(或包括 shell 循环等的 shell 代码片段),而不会因生成额外的 shell 而产生任何偏差。尝试这个:

# in Bash
time -p sleep 2 | sleep 4

您将在报告中看到 4 秒。

比较这个问题以及我的回答。

答案4

是的,但命令以;|或结尾。&&||

不,它不适用于所有复杂命令(例如a|b)或脚本。

相关内容