NAME
who
, users
— list login and system
state
SYNOPSIS
who |
-q [utmp] |
who |
[-s ]
[- {T |w }]
[-HamIL ] [-ublrpdt ]
[utmp] |
who |
[-s ]
[- {T |w }]
[-Ha IL ] [-ublrpdt ]
am I |
users |
[utmp] |
DESCRIPTION
Processes and filters the default utmpx(5) database, or the one specified in utmp.
With -q
, list names of and count currently
logged-in users to the standard output stream:
cicada cicada root cicada
# users=
4
Otherwise, filter and output system and teletype status:
Name Line Time Idle PID Comment Exit system boot 2022-07-23 23:20 5.16.0-4-amd64 root - tty1 2022-07-28 14:59 00:10 3847410 run-level 5 2022-07-23 23:34 LOGIN tty6 2022-07-23 23:42 31294 id=tty6 cicada + pts/0 2022-07-28 13:05 . 2805135 (fe80::20a:f7ff:fe63:d512%bridge1) pts/1 1004883 id=x001 term=0 exit=1 pts/2 2022-07-27 18:43 3402614 id=ts/2 term=0 exit=0
Quick Format
For each logged-in user session
(USER_PROCESS
utmpx(5) entry) output the login name
(ut_user), and a space separator. The second line
lists the amount of users in the first.
users
outputs
Quick Format, but sorts the login
names byte-wise and removes the second line.
Full Format
For each utmpx(5) entry of an enabled type (just logged-in users by default, but see OPTIONS, Full Format) write a row consisting of:
- Name
- empty for
-brdt
, otherwise the login name (ut_user); - before Line
- a space if not
USER_PROCESS
, otherwise the write status (cf. mesg(1)): - line
- The entry's teletype (ut_line), except, for:
- Time
- time of entry (ut_tv) in "
%F %R
" (YYYY-MM-DD HH:MM) format, or empty if epoch; - Idle
- empty if not
USER_PROCESS
, otherwise the time since last successful read from the teletype: - PID
- empty if
-brt
, otherwise the entry's process' PID (ut_pid); - Comment
- the inittab/entry ID as
"id=ut_id", if any,
otherwise empty, except for:
BOOT_TIME
- the ut_host field — on some systems this
is the
uname
-s
of the booted kernel, RUN_LVL OLD_TIME
,NEW_TIME
- empty, and
USER_PROCESS
- "(ut_host)" by default and if there's no stored IP/the look-up or parse failed with:
- Exit
- for
DEAD_PROCESS
, the signal and exit code: "term=ut_exit.e_termination exit=ut_exit.e_exit".
-b
&c. notation is equivalent to its
corresponding entry type (in this case BOOT_TIME
) in
the table above.
OPTIONS
am I
(or any two non-option arguments) is
equivalent to -m
.
Quick Format
-q
,--count
- Override all other flags, output Quick Format.
Full Format
-s
,--short
- If the output filter is just
USER_PROCESS
, hide the Comment column. -T
,-w
,--mesg
,--writable
,--message
- Show the write status column (before Line).
-H
,--heading
- Prepend column names as the first output line.
-a
,--all
-ublrpdt
-T
-m
- Reject entries for which ut_line is not the teletype attached to the standard input stream. (Naturally, this rejects all entries if the standard input stream is not a teletype.)
-I
,--ips
- Use the address of the
USER_PROCESS
entry as the Comment field or--lookup
target (but see the full format description above). -L
,--lookup
- Canonicalise the hostname for
USER_PROCESS
entries (likewise).
Unless any of the following are specified, the default is
USER_PROCESS
and Name Line Time
Comment:
-u
,--users
- List logged-in users (
USER_PROCESS
) and show the Idle column. -b
,--boot
- List system boots (
BOOT_TIME
, PID). -l
,--login
- List awaiting
login(8) lines (
LOGIN_PROCESS
, Idle PID). -r
,--runlevel
- List run-level changes (
RUN_LVL
, Idle PID). -p
,--process
- List live processes spawned by
init(8) (
INIT_PROCESS
, PID). -d
,--dead
- List lines with no current process (
DEAD_PROCESS
, Idle PID Exit). -t
,--time
- List clock change entries (
OLD_TIME
,NEW_TIME
, PID).
ENVIRONMENT
TZ
- Override timezone for the Time column; see tzset(3).
SEE ALSO
STANDARDS
Conforms to IEEE Std 1003.1-2024 (“POSIX.1”) — the only non-XSI-marked invocation is
who [-mTu
]USER_PROCESS
Comment matching 4.2BSD, except for
--ips
, --lookup
, which are
extensions, originating from the GNU system, and -IL
spellings, which are extensions.
The only XSI am I
spellings are such and
am i
. Some implementations allow any two arguments
or utmp after.
IEEE Std 1003.1-2008
(“POSIX.1”) specifies -s
as:
-b
Line should be »system
boot«" and the format for Idle are
somewhere between not of implementable quality and laughable. This
implementation's output format is most similar to that of the GNU system.
The Time format is "%b %e
%R
" if the LC_TIME
locale category is
C (POSIX), as required by the standard.
This is an awful format; the non-C one, specified above,
matches the GNU system. Times unrepresentable in the current time-zone are
written as plain seconds since epoch.
who
is only useful for human
eyes, do not attempt
to parse its output. Almost all semantics vary wildly across
implementations.
Without utmp,
USER_PROCESS
entries whose session leader
(ut_pid) is dead are overriden to be
DEAD_PROCESS
; this can happen if the session is not
properly terminated, or a subsession of a different user on the same
teletype. This behaviour mimicks the GNU system, which hides them entirely
instead.
The IP address of the calling remote is ut_addr_v6 under glibc, ut_ss under NetBSD, and all-0 (unavailable) elsewhere.
users
is compatible with
4.3BSD.
HISTORY
who
Research UNIX
Appears in the first edition of the UNIX Programmer's Manual as who(I):
NAME
who -- who is on the system
SYNOPSIS
who
DESCRIPTION
who lists the name, typewriter channel, and login time for each current UNIX user.
Version 2 AT&T UNIX, with the advent of wtmp(V), sees
who [ who-file ]
# who root tty8 Jan 1 00:00:15
# who /usr/adm/wtmp x Jan 1 00:02:02 root 8 Jan 1 00:02:11 8 Jan 1 00:03:32
init
, and empty names indicate logouts.
Version 4 AT&T UNIX re-adds
tty
to the with-file format.
Version 5 AT&T UNIX uses a ~ line field indicate shutdown instead.
Version 6 AT&T UNIX sees a misleading SYNOPSIS of:
who [ who-file ] [ am I ]
am I
(any two arguments); if
specified, the first line matching the teletype corresponding to the standard
input streeam is written, or "Nobody.", but the format is otherwise
equivalent.
Version 7 AT&T UNIX sees
a modernised
utmp(5) format with ut_time and
8-byte ut_line,
ut_name, with present-day semantics (and
date(1) adding
|
and
{
wtmp(5) entries for before/after clock
change); the who
format gains a space after
the name field (such that even 8-byte names are separated)
and the time format is truncated to "%b %e
%R
" — the seconds removed. In am
I
mode, if the corresponding entry wasn't found, there's just no
output, or, if the standard input stream is not a teletype, a line is
synthesised with the name equal to
getpwuid(3) of the current user (or ?), the
line to
tty??,
and the current time.
On the Interdata 7/32, the non-synthetic am
I
output line is prepended with "(Interdata)
". Under Version 7 AT&T
UNIX/32V, on the VAX, with "(Vax)
".
The BSD
4BSD uses the
<whoami.h>
sysname
macro — the system's hostname
— with the first character upper-cased, similarly wrapped in
parenthees and followed by a space, instead.
4.2BSD replaces that with
gethostname(3) and a
"hostname!"
format, producing a bang path for the current machine, It also sees the
introduction of a
16-byte
ut_host field (filled in by remote
rlogin
&c. and ftpd
for
wtmp(5)), written, if non-empty, after the time, a tab, and
in parentheses.
4.3BSD-Tahoe removes the hostname prefix
and falls back to the
getpwuid(3) username (with the current line) if nothing was
found in am I
mode.
4.3BSD-Reno sees a
"shutdown" name field for ~ entries and
"date" for
|{ ones (and moves the
files to their common modern locations at
/var/run/utmp and
/var/log/wtmp); who
is
re-written once more: the only substantive change appears to be that the
no-argument case filters by non-empty ut_name
and ut_line now (for
am I
, a nonfunctional attempt was made to only
filter out entries with empty usernames).
System V
AT&T System III UNIX inherits
Version 7 AT&T UNIX/32V
who
, except it hides the
"(Vax)
" prefix and falls back to also
probing the standard output and error streams in am
I
mode (which it allows as any argument count ≥
2).
AT&T System V Release 1
UNIX sees present-day
getutxent(3) &c. API (though without the
x
) as its primary
utmp(5) interface and
Usage: who [-rbtpludAasT] [am i] [utmp_like_file]
-u
described as "useful data") at 13x
the code size: the three optional-marked arguments can be interleft in any
order at any amount, am i
must be exactly
am i
, probes all standard I/O streams, doesn't exit
after the first found entry, and selects USER_PROCESS
(this is also the default filter, but am i
-d
is equivalent to present-day
-mud
except it produces short format);
-ublrpdt
-T
as present-day
except both -lu
enable wide format and are overriden
with -s
(actually the default),
-A
for ACCOUNTING
(no
additional data beyond Name Line Time),
-a
actually
enables all entry types (incl. EMPTY
,
yielding "Empty
slot.
") and doesn't imply -T
, the write
status is always present, but always a space unless
-T
. Empty or all-blank Name Line
fields are replaced with a single . in the middle.
The write status is considered for
USER_PROCESS
, LOGIN_PROCESS
,
INIT_PROCESS
and by
opening
/dev/ut_line write-only (and
with O_NDELAY
): if that (or closing) times out
(after 3
seconds per), ? (described as
"hung") is written; if it fails as root x is
written for "exclusive use"; - is written if the
open fails otherwise, or it succeeds as root or the invoking user is the
same as the entry's user and the
fstat(3) fails, or the line is not other-writable;
+ then means that the open succeeded for a different
user's line or mesg
-y
is
set for one's own line (or when run as root).
The idle time is considered for
USER_PROCESS
, LOGIN_PROCESS
,
DEAD_PROCESS
, INIT_PROCESS
in long mode, based on "time since the last character was sent to the
device" (modification time, in contrast to present-day's access time):
. for less than a minute (or
stat(2) failure),
old for more than a day, and
HH:MM (with
space-padded HH) otherwise.
init
overloads
RUN_LVL
ut_exit.e_termination
and ut_exit.e_exit as the current and previous
run-level, and ut_pid as the count of times it had
been at the current run-level. Those are written verbatim by
who
: centrer-aligned for Idle, at
the start of the Comment field, and left-aligned for
PID; this is written regardless of the shortness of the
output mode.
In long mode, USER_PROCESS
,
LOGIN_PROCESS
, DEAD_PROCESS
,
INIT_PROCESS
see the PID field,
right-aligned, INIT_PROCESS
,
DEAD_PROCESS
see
id=ut_id with non-printables
replaced with ^@… notation (in this case they're
/etc/lines line numbers) and
DEAD_PROCESS
term=,
exit=, as present-day but left-aligned to three columns;
USER_PROCESS
, LOGIN_PROCESS
,
instead, see (and hence the Comment
designation), the comment corresponding to their
inittab(5) entry — the ut_id
field matched to the first field with the end-of-line comment found via
"@
" or ":;
", stripped of initial blanks, and written verbatim, or the
empty string if not found or any error occurred.
No processing is done to replace or prettify the lines or users
for BOOT_TIME, RUN_LVL
,
OLD_TIME
, NEW_TIME
:
init
("system
boot"
, "run-level %c"
) and
date
("old time"
,
"new time"
) log them verbatim.
AT&T System V
Release 2 UNIX allows am I
and adds
-H
, which writes an empty line if
-abrt
(to "leave a space between stats and
output") followed by NAME LINE TIME IDLE PID COMMENTS
(or ending at TIME
in short mode) the first time it encounters a
USER_PROCESS
, LOGIN_PROCESS
,
INIT_PROCESS
, ACCOUNTING
entry, and -q
which aborts processing all flags,
selects USER_PROCESS
, and collects
ut_user fields after the EMPTY
special case and am I
filtering, but overridng all
usual processing (such that -aq
am
I
collects all ut_user fields attached to the
current teletype, but -qa
am
I
only the user ones attached to the current
teletype). There's space for a reasonable
50 users (more
segfaults), which are then bubble-sorted, broken at
80 columns,
and followed with the familiar # users=
and the
count.
AT&T System V Release 3
UNIX sees another rewrite, although largely equivalent; the usage
string is equivalent, but flags are parsed with
getopt(3), utmp_like_file must be the
only argument following flags (and is explicitly validated
for readability and being a multiple of struct utmp in
size; a if it's empty, who
exits instantly, since
"we are all done" — neglecting to write
-H
, if specified) and am I
is only recognised if they're the only arguments.
-n
# limits
the number of users per line with -q
(which no
longer has special parsing semantics); the default of 8
with 8-byte (now left-columnated) usernames yields a
default wrap of 72
columns, output during processing and therefore unsorted and unlimited.
-lrA
set long format (and override
previous -s
) and -au
set
long format but don't: -s
at the end always works.
-H
is always written before processing starts
(regardless of -q
).
am I
matches both the login username (as
obtained via
cuserid(3)) and current teletype, and exits
after writing the first matching entry.
The idle time is always checked and not just not output if the stat(2) failed; since this predates pty(7)s, much less dynamic ones this only realistically affects changed system configurations when passed wtmp(5).
The write status is derived just from the stat(2), as present-day, except the check is for other-writability.
ut_pid is also output for
OLD_TIME
, NEW_TIME
, and is
aligned for RUN_LVL
. ut_id
isn't properly limited to
4 bytes (since it's not
NUL-terminated), so it may include ut_line.
exit= isn't aligned, just has two spaces afterward. All
non-INIT_PROCESS
(instead of just
LOGIN_PROCESS
, USER_PROCESS
)
entries are checked against
inittab(5), with the comment only allowed to start with a
#
.
AT&T System V Release 4
UNIX moves both files from /etc to
/var/adm and removes -A
from
the usage string and
getopt(3) (but, curiously, keeping the rest of its option
handling), describing it as "non-functional" (for no apparent
reason, it seems to be equivalent to AT&T
System V Release 3 UNIX); on i386, if
/etc/inittab fails, it's retried as
/etc/conf/cf.d/init.base with a diagnostic,
ut_id is properly limited. Time is,
for the first time, formatted explicitly as "%b %e
%H:%M
" instead of substringing
ctime(3) to the same effect (unless in a
non-C locale).
For am I
, if no entries matched
("must be a vt", "Assuming utmp hasn't been updated with vt
name"),
utmp(5) is scanned again, this time matching only on the
username — the 178-line output function is duplicated and identical,
except it uses the saved name of the teletype attached to the standard I/O
streams instead of ut_line.
Standards
X/Open Portability Guide Issue 2 (“XPG2”) specifies
who who [ options ] [ file ] who am i who am I
name [state] line time activity pid [comment] [exit]
am I
identifying the invoking user.
-q
- writes "only the names and the number of users", ignoring all other options — this matches AT&T System V Release 4 UNIX;
-s
- "is the default and lists only the name, line and
time fields" — this is similar to
AT&T System V Release 4 UNIX to
some degree, except that
RUN_LVL
always ignores it, and-aulrA
make it not be the default or disable it; -T
- "is the same as the -u option, except that the state of
the terminal line" is written: + is writable by
anyone, - otherwise; ? means a
"bad line" — except for the
-u
part, matches AT&T System V Release 4 UNIX; -H
- adds "column headings";
-a
- "[t]urns on all options" — obviously wrong.
-u
- is as present-day and AT&T System V Release 4 UNIX, including codifying the ./HH:HH/old format for time "since activity last occurred on that particular line"
-l
- "lists only those lines on which the system is waiting for someone to login" (sic!), requiring Name to be LOGIN, and same format as for users sans the write status — this doesn't match AT&T System V Release 4 UNIX;
-d
- lists "all processes that have expired and not been respawned by init", mandating that the "exit" column contain the "the termination and exit values of the dead process" — this matches AT&T System V Release 4 UNIX;
-b
,-r
,-p
,-t
- are described as laconic filters ("time and date of the last reboot", "current run-level of the init process", "any other process which is currently active and has been previously spawned by init", "last change to the system clock") with no format requirements.
X/Open Portability Guide Issue 3
(“XPG3”) mandates, marked IN ("Internationalised
functionality", defined as optional), that the "time" column
is affected by LC_TIME
-category locale, like
AT&T System V Release 4 UNIX.
IEEE Std 1003.2a-1992 (“POSIX.2”) — User Portability Extension — sees an unrelated
who
[-mTu
]
-m
limiting for the
"current terminal", -u
additionally writing
"each displayed user's »idle time«", otherwise
entirely unspecified format-wise save for coming after
-T
, -T
writing the "state
of each terminal", conversely requiring an explicit blank-delimited
Name write status Line
Time and write status of
+-? for
"allows/denies write access to other users"/"write-access state
cannot be determined"; Time is required to be (the
AT&T System V Release 4
UNIX-identical) "%b %e %H:%M
" in the
POSIX locale.
The Rationale is a lot of
hand-wringing over AT&T System V UNIX
overloading who
beyond determining who you can
talk
to and
who
am I
command, while being one of the more intuitively
obvious UNIX system commands, had to succumb to the tide of
internationalization. It is replaced by the somewhat less charming
-m
option.The format being unspecified (and, similarly, deviations of this implementation from the standard), "[i]n such an obviously user-oriented command, designed only for human consumption" is "not considered to be a deficiency".
X/Open Portability Guide Issue 4, Version 2 (“XPG4.2”) "aligns" with IEEE Std 1003.2 (“POSIX.2”): this realistically means merging X/Open Portability Guide Issue 3 (“XPG3”), shaded EX ((XSI) "Extension") thereinto, yielding the present-day clumsy SYNOPSIS of
who [-mu
] -s
[-bHlprt
] [file]
who [-mTu
]
[-abdHlprt
] [file]
who -q
[file]
who am i
who am I
am I
as equivalent to
-m
.
IEEE Std 1003.1-2008
(“POSIX.1”) requires that Line for
BOOT_TIME
is "system boot" (and
Name is explicitly unspecified)
and moves who
to the base spec, since its User
Portability Utilities are exclusively interactive.
users
Appeared in 3BSD as users(1):
users - compact list of users who are on the system
4.3BSD skips the terminating newline if no logins were processed. This is as present-day.
4.3BSD-Tahoe sees a rewrite, always using /etc/utmp and stopping processing (with a diagnostic) when it encounters too many (now 200) non-empty login names.
4.3BSD-Reno sees another, using the
_PATH_UTMP
macro
(/var/run/utmp), deduplicating login names, and
(since they're not NUL-terminated) potentially merging subsequent ones into
the first.
4.4BSD fixes this.