如何在bash脚本中获取不同用户的$ HOME目录?

And*_*rew 58 bash

我需要以不同的用户身份执行bash脚本的一部分,并在该用户的$HOME目录中执行.但是,我不确定如何确定这个变量.切换到该用户并调用$HOME不提供正确的位置:

# running script as root, but switching to a different user...
su - $different_user
echo $HOME
# returns /root/ but should be /home/myuser
Run Code Online (Sandbox Code Playgroud)

更新:

似乎问题在于我尝试在脚本中切换用户的方式:

$different_user=deploy

# create user
useradd -m -s /bin/bash $different_user

echo "Current user: `whoami`"
# Current user: root

echo "Switching user to $different_user"
# Switching user to deploy

su - $different_user
echo "Current user: `whoami`"
# Current user: root
echo "Current user: `id`"
# Current user: uid=0(root) gid=0(root) groups=0(root)

sudo su $different_user
# Current user: root
# Current user: uid=0(root) gid=0(root) groups=0(root)
Run Code Online (Sandbox Code Playgroud)

在bash脚本中以不同用户切换用户和执行命令的正确方法是什么?

mkl*_*nt0 95

更新:根据这个问题的标题,人们似乎只是想找到一种方法来寻找不同用户的主目录,而无需冒充该用户.

在这种情况下,最简单的解决方案是使用波段扩展和感兴趣的用户名,并结合使用eval(这是必需的,因为用户名必须作为不带引号的文字给出,以便波形扩展能够工作):

eval echo "~$different_user"    # prints $different_user's home dir.
Run Code Online (Sandbox Code Playgroud)

注意:关于使用申请常见警告eval ; 在这种情况下,假设您控制其值$different_user并将其视为仅仅是用户名.

相比之下,此答案的其余部分涉及模仿用户在该用户的主目录中执行操作.


注意:

  • 默认情况下,管理员和其他用户(如果通过该sudoers文件授权)可以通过其模拟其他用户sudo.
  • 以下是基于默认配置sudo- 更改其配置可使其行为不同 - 请参阅man sudoers.

以另一个用户身份执行命令的基本形式是:

sudo -H -u someUser someExe [arg1 ...]
  # Example:
sudo -H -u root env  # print the root user's environment
Run Code Online (Sandbox Code Playgroud)

注意:

  • 如果您忽略指定-H,则模拟进程(在指定用户的上下文中调用的进程)将报告原始用户的主目录$HOME.
  • 模拟进程将具有与调用进程相同的工作目录.
  • 模拟进程不会对作为参数传递的字符串文字执行shell扩展,因为模拟进程中不涉及shell(除非someExe碰巧是shell) - 调用 shell的扩展- 在传递给模拟进程之前 - 显然仍然会发生.

(可选)您可以使用或通过(n模仿)shell运行模拟进程,前缀someExe-i -s - 不指定someExe ...创建交互式 shell:

  • -i创建一个登录 shell someUser,其中包含以下内容:

    • someUser用户特定的壳轮廓,如果定义的话,是装载.
    • $HOME指向someUser的主目录,所以没有必要-H(尽管你仍然可以指定它)
    • 模拟shell的工作目录是someUser主目录.
  • -s 创建一个非登录shell:

    • 没有加载shell 配置文件(虽然交互式非登录 shell的初始化文件是;例如,~/.bashrc)
    • 除非您还指定-H,否则模拟过程将报告原始用户的主目录$HOME.
    • 模拟shell将具有与调用进程相同的工作目录.

使用shell意味着在命令行上传递的字符串参数可能受到模拟shell的shell扩展(参见下面的特定于平台的差异)(可能在调用shell初始扩展之后); 比较以下两个命令(使用引号来防止调用 shell 过早扩展):

  # Run root's shell profile, change to root's home dir.
sudo -u root -i eval 'echo $SHELL - $USER - $HOME - $PWD'
  # Don't run root's shell profile, use current working dir.
  # Note the required -H to define $HOME as root`s home dir.
sudo -u root -H -s eval 'echo $SHELL - $USER - $HOME - $PWD'
Run Code Online (Sandbox Code Playgroud)

调用的shell由"SHELL环境变量(如果已设置)或shell(在passwd(5)中指定")(根据man sudo)确定.请注意,-s这是在调用用户的环境是重要的,而与-i它是模拟的用户.

请注意,有关shell相关行为的平台差异(使用-i-s):

  • sudoLinux上显然只接受可执行文件或内置名称作为/ 后面的第一个参数-s-i,而OSX允许传递整个shell命令行; 例如,OSX sudo -u root -s 'echo $SHELL - $USER - $HOME - $PWD'直接接受(不需要eval),而Linux则不接受(截至sudo 1.8.95p).

  • sudoLinux上的旧版本不会将shell扩展应用于传递给shell的参数 ; 例如,使用sudo 1.8.3p1(例如,Ubuntu 12.04),sudo -u root -H -s echo '$HOME'只需回显字符串文字"$ HOME",而不是在root用户的上下文中扩展变量引用.至少sudo 1.8.9p5(例如,Ubuntu 14.04),这已得到修复.因此,为了确保在Linux上进行扩展,即使使用旧sudo版本,也要将整个命令作为单个参数传递给eval; 例如:sudo -u root -H -s eval 'echo $HOME'.(虽然在OSX上没有必要,但这也适用于那里.)

  • root用户的$SHELL变量包含/bin/sh在OSX 10.9,而这是/bin/bash在Ubuntu 12.04.

无论是冒充过程涉及壳与否,它的环境将有以下变量设置,反映调用用户和命令:SUDO_COMMAND,SUDO_USER,SUDO_UID=,SUDO_GID.

man sudoman sudoers更多的细微之处.

向@DavidW和@Andrew提供灵感的帽子.


Dav*_* W. 21

在BASH中,您可以$HOME通过在用户的登录ID前加上波形符号来查找用户的目录.例如:

$ echo ~bob
Run Code Online (Sandbox Code Playgroud)

这将回显用户bob$HOME目录.

但是,您说您希望能够以特定用户身份执行脚本.为此,您需要设置sudo.此命令允许您以特定用户身份执行特定命令.例如,以foo用户身份执行bob:

$ sudo -i -ubob -sfoo
Run Code Online (Sandbox Code Playgroud)

这将启动一个新shell,并将-i使用用户的默认环境和shell模拟登录(这意味着foo命令将从bob's$ HOME`目录执行.)

Sudo设置有点复杂,你需要成为超级用户才能看到颤抖的文件(通常/etc/sudoers).但是,此文件通常有几个可以使用的示例.

在此文件中,您可以指定您指定的可以运行命令的命令,用户以及该用户在执行该命令之前是否必须输入其密码.这通常是默认设置(因为它证明了这是用户而不是在用户获取可口可乐时过来的人.)但是,当您运行shell脚本时,通常需要禁用此功能.


Tri*_*onX 18

为了寻找轻量级方法找到用户家庭目录的人的替代答案......

而不是乱搞su黑客,或打扰发起另一个bashshell 的开销只是为了找到$HOME环境变量...

通过Bash轻量级简单Homedir查询

有一个专门针对此的命令: getent

getent passwd someuser | cut -f6 -d:

getent可以做更多...只需看到手册页.在passwdnsswitch的数据库将返回用户在录入/etc/passwd格式.只需将它拆分在冒号上:即可解析出字段.

应该安装在大多数Linux系统(或使用任何系统GNU库Ç(RHEL: glibc-common,德布:libc-bin)

  • 正如你所说,`getent`特定于Linux; 一个更简单和平台中立的方法是使用bash的[代码扩展](https://www.gnu.org/software/bash/manual/html_node/Tilde-Expansion.html)和感兴趣的用户名(`eval`是需要,因为用户名必须作为_unquoted literal_给出,以便扩展工作):`eval echo"〜$ different_user"` - 通常的警告重新使用`eval` apply. (2认同)
  • @DavidTonhofer:使用“getent”而不是仅从“/etc/passwd”读取的原因是因为用户和登录身份验证数据后端可能存在不同的来源。用户数据库有多种后端,例如“sssd”(sss/LDAP)、NIS 和 NIS+。有关这方面的更多信息,请参阅[有关 nsswitch.conf 的 SE 答复](https://unix.stackexchange.com/a/171827/7688)。我同意,对额外剪切的需求并不是最大的,但这就是工具的输出。使用“getent”的真正原因是尝试查询系统上配置的用户数据库后端。 (2认同)

Bru*_*sky 5

这个问题的标题是如何在bash脚本中获取不同用户的$HOME目录?这就是人们从谷歌来这里寻找答案的原因。

有一个安全的方法可以做到这一点!

在 Linux/BSD/macOS/OSX 上无需 sudo 或 root

user=pi
user_home=$(bash -c "cd ~$(printf %q $USER) && pwd")
Run Code Online (Sandbox Code Playgroud)

注意:之所以安全,是因为 bash(甚至 4.4 之前的版本)有自己的printf功能,其中包括:

%q quote the argument in a way that can be reused as shell input

看:help printf

比较此处其他答案如何响应代码注入

# "ls /" is not dangerous so you can try this on your machine
# But, it could just as easily be "sudo rm -rf /*"
$ user="root; ls /"
$ printf "%q" "$user"
root\;\ ls\ /

# This is what you get when you are PROTECTED from code injection
$ user_home=$(bash -c "cd ~$(printf "%q" "$user") && pwd"); echo $user_home
bash: line 0: cd: ~root; ls /: No such file or directory

# This is what you get when you ARE NOT PROTECTED from code injection
$ user_home=$(bash -c "cd ~$user && pwd"); echo $user_home
bin boot dev etc home lib lib64 media mnt ono opt proc root run sbin srv sys tmp usr var /root

$ user_home=$(eval "echo ~$user"); echo $user_home
/root bin boot dev etc home lib lib64 media mnt ono opt proc root run sbin srv sys tmp usr var
Run Code Online (Sandbox Code Playgroud)

在 Linux/BSD/macOS/OSX 上以 root 身份运行

如果你这样做是因为你正在运行某些东西,root那么你可以使用 sudo 的力量:

user=pi
user_home=$(sudo -u $user sh -c 'echo $HOME')
Run Code Online (Sandbox Code Playgroud)

在 Linux/BSD 上(但不是现代 macOS/OSX),无需 sudo 或 root

如果没有,您可以从 获取/etc/passwd。已经有很多使用evaland的例子getent,所以我将给出另一种选择:

user=pi
user_home=$(awk -v u="$user" -v FS=':' '$1==u {print $6}' /etc/passwd)
Run Code Online (Sandbox Code Playgroud)

如果我有一个包含许多其他 awk oneliners 的 bash 脚本并且不使用cut. 虽然许多人喜欢“编码高尔夫”以使用最少的字符来完成任务,但我更喜欢“工具高尔夫”,因为使用更少的工具可以使您的脚本具有更小的“兼容性足迹”。此外,您的同事或未来的自己也不需要阅读手册页来理解它。