在通过 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>
sudo su - :
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 ]#
sudo su -; su tom :
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$
没有一个完美的答案。当您更改用户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
那么结果就是“没有登录名
”,因为输入不是终端。更有趣的是,用户可以冒充另一个已登录的用户。由于Joe在pts/1上登录,Tom可以通过运行来冒充他。
logname < /dev/pts1
现在,它显示joe
,尽管是tom运行了命令。换句话说,如果你在任何安全角色中使用这种机制,那就疯了。
pstree
命令为我们完成了这种排列。pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1 | sed 's/[()]//g'
pstree
展示了运行中的进程树。该树以一个pid为根,这里给出的是$$
,在bash中扩展为当前shell的进程id。因此,命令的第一部分列出了当前shell的所有祖先进程,并带有一些有趣的格式。命令的其余部分丢弃了有趣的格式,以挑选出拥有最老的祖先进程的用户名称。
与其他基于pstree
的答案相比,主要改进之处在于输出中不包含多余的括号。
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
有效 - 将结果添加在上方 - evan$LOGNAME
,但没有起作用。同时将其添加到上面的结果中。 - evanlogname
命令是否仍需要 tty?根据我的测试,它总是通过的。(也许我做错了什么。)我正在运行带有 coreutils 8.26 的 Linux。 - simohesystemd-logind
的系统上,Systemd API提供了这些信息。如果您想从shell脚本访问此信息,需要使用类似以下内容:$ loginctl session-status \
| (read session_id ignored; loginctl show-session -p User $session_id)
User=1000
< p > loginctl
的 session-status
和 show-session
系统命令在没有参数的情况下具有不同的行为: session-status
使用当前会话,而 show-session
使用管理器。然而,由于其机器可读输出,对于脚本使用来说,使用 show-session
更可取。这就是为什么需要两次调用 loginctl
的原因。
将user1683793的findUser()函数移植到中,并扩展其功能,使其可以返回存储在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"
多次调用ps的替代方案:只需进行一次pstree调用
pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1
输出(以even身份登录时):(evan)
pstree参数:
使用grep -o
和head
获取第一个用户更改(即登录)。
限制:命令中不得包含任何大括号()
(通常不会)
根据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
等列表中。
我知道这个问题很久以前就有了,但像我这样的人仍在询问,而这似乎是一个好地方来提供解决方案。
who | awk '{print $1}'
。 - SiegeXwho am i
和who smells bad
是相同的。但是,仅当STDIN
与TTY相关联时它才能起作用。因此,如果你运行echo "hello" | who am i
,它就不会起作用。 - tylerlecho "hello" | who am i
,除非您的脚本在没有终端的环境中运行。然后,您可能会看到错误,即who am i
无法工作,因为非可读stdin存在某种问题,在这种情况下,您可能会尝试通过将数据管道传输到who am i
来满足其stdin要求。tylerl只是指出他已经走过了那条路,而管道不起作用,因为stdin必须既可读又与TTY相关联。 - Edwin Bucklogname
,结果证明它确实有效,而who am i
则不行。 - Bart van Heukelom