如何使用su以该用户身份执行bash脚本的其余部分?


126

我编写了一个脚本,该脚本以一个字符串作为参数,该字符串是用户名和项目的串联。该脚本应该根据项目字符串切换(su)为用户名,cd为特定目录。

我基本上想做的是:

su $USERNAME;  
cd /home/$USERNAME/$PROJECT;  
svn update;  

问题是,一旦我执行su操作,它就会在那里等待。这是有道理的,因为执行流程已转到切换到用户。我退出后,其余的所有事情都会执行,但无法按预期进行。

我在su之前添加了svn命令,但是该命令失败了(即它没有更新所需目录中的svn)。

如何编写允许用户切换用户并调用svn的脚本?

Answers:


86

诀窍是使用“ sudo”命令代替“ su”

您可能需要添加此

username1 ALL=(username2) NOPASSWD: /path/to/svn

到您的/ etc / sudoers文件

并将脚本更改为:

sudo -u username2 -H sh -c "cd /home/$USERNAME/$PROJECT; svn update" 

其中username2是您要运行SVN命令的用户,而username1是运行脚本的用户。

如果您需要多个用户来运行此脚本,请使用%groupname代替用户名1


我有一个类似的问题,但我想chsh为其他用户运行。我的问题在此处列出在stackoverflow.com/q/15307289/80353中,如何根据您的情况调整您的答案?
Kim Stacks 2013年

我这样做了-但它仍然要求我输入密码。
嬉皮士(Hippyjim)2013年

@Hippyjim确定要正确输入用户名吗?
威2013年

1
我做到了-事实证明,我还需要允许使用/ bin / bash。
2013年

3
无论您使用sudo还是su次要的,它sudo都更加安全和方便。
2015年

105

简单得多:用于sudo运行Shell并使用Heredoc向其提供命令。

#!/usr/bin/env bash
whoami
sudo -i -u someuser bash << EOF
echo "In"
whoami
EOF
echo "Out"
whoami

最初在SuperUser上回答)


5
这个答案效果最好。我建议使用option -i获得someuser的预期环境。
not2savvy

2
sudo可能很方便,但是如果不开箱即用(例如在AIX上),那就不好了。su -c 'commands'是正确的答案。
Tricky

1
史诗般的解决方案!
3bdalla

如何使变量在Herdoc范围内可用?
dotlashlulu

在AIX上,它将引发以下错误ksh:sh:0403-006执行许可被拒绝。
艾哈迈德·拉纳(AhmedRana)

54

使用类似于以下的脚本在另一个用户下执行脚本的其余部分:

#!/bin/sh

id

exec sudo -u transmission /bin/sh - << eof

id

eof

11
您可能要使用“ sudo -i -u ...”来确保正确设置了诸如$ HOME之类的东西。
布莱恩·拉尔森

抱歉,菜鸟有问题,但是这里的ID是什么?
Nitin Jadhav

1
@NitinJadhav,他在这里使用它只是为了显示当前用户的ID,root的ID为0,因此第一个ID将显示一些数字,但是第二个ID肯定显示为0(因为第二个ID是在内部执行的)由root运行的块)。您可以使用user whoami代替,id而是返回名称而不是id
Mohammed Noureldin

@MohammedNoureldin谢谢!
Nitin Jadhav

sudo可能很方便,但是如果不开箱即用(例如在AIX上),那就不好了。su -c 'commands'是正确的答案。
Tricky

52

您需要将所有不同用户的命令作为自己的脚本执行。如果只是一个或几个命令,那么内联应该起作用。如果命令很多,最好将它们移动到自己的文件中。

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 

4
这是唯一正确的答案。sudo对此不是必需的。
helvete '16

1
您可能还需要为shell提供su -s /bin/bash
mixel

超级

对于那些默认情况下未安装sudo的人来说,最佳答案(我在看您的AIX)
Tricky

46

这是另一种方法,在我的情况下更方便(我只想放弃root特权,并从受限用户处执行脚本的其余部分):您可以使脚本从正确的用户处重新启动。假设它最初是作为root运行的。然后它将如下所示:

#!/bin/bash
if [ $UID -eq 0 ]; then
  user=$1
  dir=$2
  shift 2     # if you need some other parameters
  cd "$dir"
  exec su "$user" "$0" -- "$@"
  # nothing will be executed beyond that line,
  # because exec replaces running process with the new one
fi

echo "This will be run from user $UID"
...

6
我不知道为什么这不是更高的评价。它是最好的解决原始问题的方法,同时又可以使所有内容保持最佳状态。
SirVer 2013年

或者runuser -u $user -- "$@",如su(1)
cghislai

1
exec su "$user" "$0" -- "$@"
macieksk

1
谢谢@macieksk,不错的收获。将更新。将--是非常有用的。
MarSoft

1
提出的方法是最好的,并且是完整的。全部都用单个脚本编写,并且全部使用bash。实际上,此脚本运行两次。在最初的脚本测试中,它是root用户,然后准备环境,然后按su更改用户。但是用exec命令来做,那么只有一个脚本实例。在第二个循环中,如果省略了if / fi短语,则脚本直接执行准备的操作。在此示例中,它是回声。所有其他方法只能部分解决问题。当然,该脚本可以在sh而不是bash下运行,但是我们必须测试$ HOME特殊变量,而不是$ UID
Znik

7

使用sudo替代

编辑:正如道格拉斯指出的那样,您不能使用它cdsudo因为它不是外部命令。您必须在子shell中运行命令才能进行cd工作。

sudo -u $USERNAME -H sh -c "cd ~/$PROJECT; svn update"

sudo -u $USERNAME -H cd ~/$PROJECT
sudo -u $USERNAME svn update

可能会要求您输入该用户的密码,但只能输入一次。


但是,这将不起作用-在第一个sudo完成执行后,cd将丢失。
Douglas Leeder

实际上,您甚至不能直接调用cd,因为它不是外部命令。
iamamac

sudo可能很方便,但是如果不开箱即用(例如在AIX上),那就不好了。su -c 'commands'是正确的答案。
Tricky

6

无法在Shell脚本中更改用户。其他答案中所述的使用sudo的变通办法可能是最好的选择。

如果您疯狂地以root用户身份运行perl脚本,则可以使用包含$< $( $> $) 真实/有效uid / gid 的变量来执行此操作,例如:

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');

-1,因为它IS可以用sudo来临时获得其他用户的权限。

4
从某种意义上讲,这是不可能的,因为用户无法更改shell脚本本身的运行方式(这是原始问题所要求的)。使用sudo调用其他进程不会更改脚本本身的运行方式。
P-Nuts 2010年

2

这对我有用

我从“启动”中拆分了“配置”。

 # Configure everything else ready to run 
  config.vm.provision :shell, path: "provision.sh"
  config.vm.provision :shell, path: "start_env.sh", run: "always"

然后在我的start_env.sh中

#!/usr/bin/env bash

echo "Starting Server Env"
#java -jar /usr/lib/node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-2.40.0.jar  &
#(cd /vagrant_projects/myproj && sudo -u vagrant -H sh -c "nohup npm install 0<&- &>/dev/null &;bower install 0<&- &>/dev/null &")
cd /vagrant_projects/myproj
nohup grunt connect:server:keepalive 0<&- &>/dev/null &
nohup apimocker -c /vagrant_projects/myproj/mock_api_data/config.json 0<&- &>/dev/null &

-2

受@ MarSoft的想法启发,但我更改了如下代码:

USERNAME='desireduser'
COMMAND=$0
COMMANDARGS="$(printf " %q" "${@}")"
if [ $(whoami) != "$USERNAME" ]; then
  exec sudo -E su $USERNAME -c "/usr/bin/bash -l $COMMAND $COMMANDARGS"
  exit
fi

我曾经sudo允许使用较少的密码来执行脚本。如果要输入用户密码,请删除sudo。如果不需要环境变量,请-E从sudo中删除。

/usr/bin/bash -l保证,该profile.d脚本用于初始化的环境中执行。


sudo可能很方便,但是如果不开箱即用(例如在AIX上),那就不好了。su -c 'commands'是正确的答案。
Tricky

@Tricky也许阅读完整答案,它已经建议删除sudo。实际上,sudo并不是那么简单,在很多情况下,您甚至需要偶数,sudo -E并且需要在sudoers.d中添加一个配置条目以允许使用tty而无需执行!requiretty。但是在很多情况下,对于自动调用的脚本,必须使用sudo,否则密码对话框可能会受到干扰。因此,我不会将其从标准解决方案中删除。
Trendfischer

有问题的代码是完全错误的-它将用空格破坏脚本名称或参数;请记住,放入"$@"字符串意味着将第一个参数之后的参数附加到一个单独的字符串中,而不包含在-c参数中。如果您想使其安全,可以printf -v arg_q '%q ' "$0" "$@"然后使用su "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Charles Duffy

@CharlesDuffy你是对的!我为这个答案简化了生产脚本:-(只是添加一个COMMANDARGS=$@解决了-c。的问题,以前没有问题,但是我实现了您的良好输入。我只需要做一些实验就可以使它起作用。已经编辑了问题,希望我不会再粘贴其他错误。感谢您的评论,让您
感到
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.