【发布时间】:2011-06-03 15:16:53
【问题描述】:
通过 sudo 或 su 运行脚本时,我想获取原始用户。无论多个sudo 或su 在彼此内部运行,特别是sudo su -,都应该发生这种情况。
【问题讨论】:
通过 sudo 或 su 运行脚本时,我想获取原始用户。无论多个sudo 或su 在彼此内部运行,特别是sudo su -,都应该发生这种情况。
【问题讨论】:
结果:
使用who am i | awk '{print $1}' 或logname,因为不保证其他方法。
以自己的身份登录:
evan> echo $USER
evan
evan> echo $SUDO_USER
evan> echo $LOGNAME
evan
evan> whoami
evan
evan> who am i | awk '{print $1}'
evan
evan> logname
evan
evan>
普通sudo:
evan> sudo -s
root> echo $USER
root
root> echo $SUDO_USER
evan
root> echo $LOGNAME
root
root> whoami
root
root> who am i | awk '{print $1}'
evan
root> logname
evan
root>
须藤苏-:
evan> sudo su -
[root ]# echo $USER
root
[root ]# echo $SUDO_USER
[root ]# echo $LOGNAME
root
[root ]# whoami
root
[root ]# who am i | awk '{print $1}'
evan
[root ]# logname
evan
[root ]#
须藤苏-;总结:
evan> sudo su -
[root ]# su tom
tom$ echo $USER
tom
tom$ echo $SUDO_USER
tom$ echo $LOGNAME
tom
tom$ whoami
tom
tom$ who am i | awk '{print $1}'
evan
tom$ logname
evan
tom$
【讨论】:
who | awk '{print $1}'
who am i 与 who smells bad 相同。此外,它仅在 STDIN 与 TTY 关联时才有效。所以如果你运行echo "hello" | who am i,它根本就行不通。
echo "hello" | who am i,除非你的脚本在没有终端的环境中运行。然后您可能会看到who am i 不起作用的错误,因为不可读的标准输入存在某种问题,在这种情况下,您可能会绝望地尝试将数据管道输入who am i 以满足它的标准输入要求。 tylerl 只是注意到他已经走上了这条路,并且管道将无法工作,因为 stdin 必须既可读又与 TTY 相关联。
logname,事实证明它确实有效,而who am i 则不行.
没有完美的答案。当您更改用户 ID 时,通常不会保留原始用户 ID,因此会丢失信息。一些程序,例如logname 和who -m 实现了一个hack,它们检查哪个终端连接到stdin,然后检查哪个用户在该终端上登录。
此解决方案经常有效,但并非万无一失,当然也不应该被视为安全。例如,假设who 输出以下内容:
tom pts/0 2011-07-03 19:18 (1.2.3.4)
joe pts/1 2011-07-03 19:10 (5.6.7.8)
tom 使用su 进入root,并运行您的程序。如果STDIN 没有被重定向,那么像logname 这样的程序将输出tom。如果它被重定向(例如从文件中):
logname < /some/file
那么结果是“no login name”,因为输入不是终端。然而,更有趣的是,用户可以伪装成不同的登录用户。由于 Joe 在 pts/1 上登录,Tom 可以通过运行来伪装成他
logname < /dev/pts1
现在,它显示 joe,尽管运行该命令的是 tom。换句话说,如果你在任何类型的安全角色中使用这种机制,你就疯了。
【讨论】:
这是我在 HP-UX 上编写的 ksh 函数。我不知道它如何在 Linux 中与 Bash 一起工作。这个想法是sudo 进程作为原始用户运行,子进程是目标用户。通过循环返回父进程,我们可以找到原始进程的用户。
#
# The options of ps require UNIX_STD=2003. I am setting it
# in a subshell to avoid having it pollute the parent's namespace.
#
function findUser
{
thisPID=$$
origUser=$(whoami)
thisUser=$origUser
while [ "$thisUser" = "$origUser" ]
do
( export UNIX_STD=2003; ps -p$thisPID -ouser,ppid,pid,comm ) | grep $thisPID | read thisUser myPPid myPid myComm
thisPID=$myPPid
done
if [ "$thisUser" = "root" ]
then
thisUser=$origUser
fi
if [ "$#" -gt "0" ]
then
echo $origUser--$thisUser--$myComm
else
echo $thisUser
fi
return 0
}
我知道最初的问题是很久以前提出的,但人们(比如我)仍在询问,这看起来是一个解决问题的好地方。
【讨论】:
使用 logname(1) 获取用户的登录名怎么样?
【讨论】:
logname(1) 不起作用,但 logname 起作用 - 添加上面的结果
$LOGNAME,但没有成功。也添加到上面的结果中。
logname 还需要 tty 吗?通过我的测试,它总是通过。 (也许我出了点问题。)我正在使用 coreutils 8.26 运行 linux。
ORIGIONAL_USER=$(pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1 | sed 's/[()]//g') && echo ${ORIGIONAL_USER}
这是唯一对我有用的东西。
pstree 将正在运行的进程显示为树。该树以 pid 为根,这里以 $$ 给出,在 bash 中扩展为当前 shell 的进程 ID。所以命令的第一部分用一些有趣的格式列出了当前 shell 的所有祖先进程。该命令的其余部分丢弃了有趣的格式以挑选出拥有最古老祖先进程的用户的名称。
与其他基于pstree 的答案相比,这里的主要改进是输出中不包含多余的括号。
【讨论】:
user1683793 的 findUser() 函数移植到 bash 并扩展,因此它也返回存储在 NSS 库中的用户名。
#!/bin/bash
function findUser() {
thisPID=$$
origUser=$(whoami)
thisUser=$origUser
while [ "$thisUser" = "$origUser" ]
do
ARR=($(ps h -p$thisPID -ouser,ppid;))
thisUser="${ARR[0]}"
myPPid="${ARR[1]}"
thisPID=$myPPid
done
getent passwd "$thisUser" | cut -d: -f1
}
user=$(findUser)
echo "logged in: $user"
【讨论】:
循环返回并给出用户列表
基于 user1683793 的回答
通过排除非 TTY 进程,我跳过 root 作为登录的发起者。我不确定在某些情况下这是否会排除太多
#!/bin/ksh
function findUserList
{
typeset userList prevUser thisPID thisUser myPPid myPid myTTY myComm
thisPID=$$ # starting with this process-ID
while [ "$thisPID" != 1 ] # and cycling back to the origin
do
( ps -p$thisPID -ouser,ppid,pid,tty,comm ) | grep $thisPID | read thisUser myPPid myPid myTTY myComm
thisPID=$myPPid
[[ $myComm =~ ^su ]] && continue # su is always run by root -> skip it
[[ $myTTY == '?' ]] && continue # skip what is running somewhere in the background (without a terminal)
if [[ $prevUser != $thisUser ]]; then # we only want the change of user
prevUser="$thisUser" # keep the user for comparing
userList="${userList:+$userList }$thisUser" # and add the new user to the list
fi
#print "$thisPID=$thisUser: $userList -> $thisUser -> $myComm " >&2
done
print "$userList"
return 0
}
logname 或 who am i 没有给我想要的答案,尤其是在较长的su user1、su user2、su user3、... 列表中
我知道最初的问题是很久以前提出的,但人们(比如我)仍然在问,这看起来是提出解决方案的好地方。
【讨论】:
多次调用 ps 的替代方法:执行一次 pstree 调用
pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1
输出(以偶数登录时):(evan)
pstree 参数:
使用grep -o 和head 获取第一个用户更改(即登录)。
限制:该命令不能包含任何大括号()(通常不包含)
【讨论】:
在运行 systemd-logind 的系统上,systemd API provides this information。如果你想从一个 shell 脚本中访问这些信息,需要使用这样的东西:
$ loginctl session-status \
| (read session_id ignored; loginctl show-session -p User $session_id)
User=1000
loginctl 的 session-status 和 show-ssession 系统命令在没有参数的情况下具有不同的行为:session-status 使用当前会话,但 show-ssession 使用管理器。但是,由于其机器可读的输出,使用 show-session 更适合脚本使用。这就是为什么需要两次调用loginctl。
【讨论】:
您可以从控制终端的所有者那里获得它。这是“whowasi”实用程序的旧 C 代码:http://sivann.gr/software/whowasi.c
【讨论】: