【问题标题】:Why does sudo behave like this when the command has a variable assignment and what can I do?当命令具有变量赋值时,为什么 sudo 会出现这样的行为,我该怎么办?
【发布时间】:2013-03-03 13:03:12
【问题描述】:

我正在调试一个 bash 脚本,并将我的问题归结为这个非常简单的脚本。它在 CentOS 5.7 中运行:

# tmp.sh
function do_as_someuser {
  sudo -u someuser -i "$1"
}

do_as_someuser "ls -l"       # line 1
#do_as_someuser "A=1 ls -l"  # line 2

当我执行这个脚本只取消注释第 1 行时,它显示 someuser 的主目录列表,正如预期的那样:

[root@centos57 test]# ./tmp.sh 
total 4
drwxr-xr-x 4 someuser someuser 4096 Mar 14 23:36 some_dir
[root@centos57 test]# 

但是当我只取消注释第 2 行时,它会将我记录为 someuser 并且不会返回到我的用户提示符,这绝对不是我所期望的。到底为什么要定义一个变量?

[root@centos57 test]# ./tmp.sh 
[someuser@centos57 ~]$ 

A=1 ls -l 在我直接在命令行中编写时运行得非常好。如果这是一个有效的命令,我当然应该能够将它作为参数传递给sudo 来执行,不是吗?

我的问题:

  • 为什么会这样?
  • 如何修改do_as_someuser,使其执行具有变量赋值的命令?

【问题讨论】:

    标签: bash centos centos5


    【解决方案1】:

    认为正在发生的事情是sudo 本身将“var=value”形式解释为环境变量赋值,而不是仅仅将其传递给 shell。就sudo 而言,您将A 分配为具有1 ls -l 的值,然后不运行命令。例如,这是一个快速控制台会话:

    $ sudo -i "X=5 ls"
    root# echo $X
    5 ls
    

    根据快速测试,您可以在要运行的命令之前使用-- 选项来避免该问题:

    $ sudo -i -- "X=5 ls"
    ... files ...
    $ sudo -i -- 'BLORT=zorch env' | grep BLORT
    BLORT=zorch
    SUDO_COMMAND=/bin/sh -c BLORT=zorch env
    

    【讨论】:

      【解决方案2】:

      实现你想要的一种方法是使用

      sudo -u someuser $SHELL -c "A=1; ls -l; echo \$A"
      

      即在您作为该用户执行的 shell 命令 的开头将要穿梭的变量的赋值放入其他用户上下文中。

      一个更易读的替代方案来证明这一点:

      sudo -u someuser $SHELL -c "FOOBAR=BAZ env|grep FOOBAR"
      

      为什么会这样?我认为这是因为 sudo 没有使用 shell 作为中介来执行你给定的命令。然而,这就是我上面给出的命令的作用。从带有-i 的错误消息中,我假设bash(或者在任何情况下来自passwd 的登录shell)正在尝试exec(3) 命令而不是拆分参数 - 坦率地说,它怎么知道如何无论如何要正确拆分它?:

      $ sudo -u someuser $SHELL -i "FOOBAR=1 env|grep FOOBAR"
      bash: FOOBAR=1 env|grep FOOBAR: No such file or directory
      

      但我认为sudo 的手册页在这种情况下的实现是错误的(-i 选项的文档):

      如果指定了命令,它会通过 shell 的 -c 选项传递给 shell 以执行。

      如您所见,由于使用了必须转义的变量,因此上述操作并不十分方便。

      继续你的例子:

      # tmp.sh
      function do_as_someuser {
          sudo -u someuser $SHELL -c "$1"
      }
      
      do_as_someuser "ls -l"       # line 1
      do_as_someuser "A=1 ls -l"   # line 2
      

      我从您的问题中不明白的部分是为什么您在期望命令运行并返回时坚持使用-i...为什么 initial login shell 那么呢? 你需要来自.profile 等的东西吗?其他人?

      【讨论】:

      • 您好,感谢您的回答。关于-i,您的观察是正确的,不需要。有趣的是,我的原始示例没有 -i 在使用第 2 行时会出现语法错误,即使 sudo -u user command 原则上应该是有效的。
      • 另外,我对您的示例 $ sudo -u someuser $SHELL -i "FOOBAR=1 env|grep FOOBAR" 感到困惑,因为您会将 -i 选项传递给 $SHELL,而不是 sudo。
      • @Jong Bor Lee:正确,在bash的情况下-i具有一定的含义。当然,假设 $SHELL 将评估为 bash 是有些错误的。
      • 我认为 $ sudo -u someuser $SHELL -i "FOOBAR=1 env|grep FOOBAR" 失败了,不是因为它不会像你说的那样被正确解析,而是因为它所做的是执行 $SHELL 传递选项 -i (在大多数 shell 中是交互式的) 和一个不存在的脚本文件名“FOOBAR=1 env|grep FOOBAR”。所以我不确定你的解释是否来自这个例子......(或者我误读了你的意思......)
      • @Jong Bor Lee:关于这个确切的例子,我写的很重要:从带有 -i 的错误消息中,我假设 bash (或者无论如何登录 shell passwd) 正在尝试 exec(3) 命令而不是拆分参数 - 坦率地说,它怎么知道如何正确拆分它?
      猜你喜欢
      • 2021-04-27
      • 1970-01-01
      • 2013-05-17
      • 2021-06-13
      • 2013-12-03
      • 1970-01-01
      • 1970-01-01
      • 2014-12-15
      • 2019-09-13
      相关资源
      最近更新 更多