WHO(1) General Commands Manual Fully-rendered PDF

who, userslist login and system state

who -q [utmp]
who [-s] [-{T|w}] [-HamIL] [-ublrpdt] [utmp]
who [-s] [-{T|w}] [-Ha IL] [-ublrpdt] am I
users [utmp]

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:

empty for -brdt, otherwise the login name (ut_user);
before Line
a space if not USER_PROCESS, otherwise the write status (cf. mesg(1)):
if allowed (g+w),
-
if blocked, or
if stat(/dev/ut_line) failed;
The entry's teletype (ut_line), except, for:
"system boot"
"run-level", followed by a space and the lowest byte of ut_pid — the new run-level — if non-zero
"date before"
"date after";
time of entry (ut_tv) in "%F %R" (YYYY-MM-DD HH:MM) format, or empty if epoch;
empty if not USER_PROCESS, otherwise the time since last successful read from the teletype:
if under a minute,
HH:MM
if under a day,
otherwise, and
if the aforementioned () failed;
empty if -brt, otherwise the entry's process' PID (ut_pid);
the inittab/entry ID as "id=ut_id", if any, otherwise empty, except for:
the ut_host field — on some systems this is the uname -s of the booted kernel,
, NEW_TIME
empty, and
"ut_host" by default and if there's no stored IP/the look-up or parse failed with:
the IP address of the calling remote,
the canonical DNS name of the IP address of the calling remote,
 -L
the canonical DNS name of the calling remote's hostname (ut_host), parenthesised;
for DEAD_PROCESS, the signal and exit code: "term=ut_exit.e_termination exit=ut_exit.e_exit".
The -b &c. notation is equivalent to its corresponding entry type (in this case BOOT_TIME) in the table above.

am I (or any two non-option arguments) is equivalent to -m.

Quick Format

, --count
Override all other flags, output Quick Format.

Full Format

, --short
If the output filter is just USER_PROCESS, hide the Comment column.
, -w, --mesg, --writable, --message
Show the write status column (before Line).
, --heading
Prepend column names as the first output line.
, --all
-T
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.)
, --ips
Use the address of the USER_PROCESS entry as the Comment field or --lookup target (but see the full format description above).
, --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:

, --users
List logged-in users (USER_PROCESS) and show the Idle column.
, --boot
List system boots (BOOT_TIME, PID).
, --login
List awaiting login(8) lines (LOGIN_PROCESS, Idle PID).
, --runlevel
List run-level changes (RUN_LVL, Idle PID).
, --process
List live processes spawned by init(8) (INIT_PROCESS, PID).
, --dead
List lines with no current process (DEAD_PROCESS, Idle PID Exit).
, --time
List clock change entries (OLD_TIME, NEW_TIME, PID).

Override timezone for the Time column; see tzset(3).

finger(1), mesg(1), pinky(1), w(1), write(1), utmpx(5)

Conforms to IEEE Std 1003.1-2024 (“POSIX.1”) — the only non-XSI-marked invocation is

who [-mTu]
and the output is
in an implementation-defined format, subject only to the requirement of containing the information described above.
The XSI-marked bits are (largely) equivalent to this implementation's invocation and are similar to AT&T System V Release 3 UNIX with 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:

List only the <name>, <line>, and <time> fields. This is the default case.
It isn't in this implementation and the GNU system. This isn't an issue since the requirements, even on XSI output, beyond "it should be in these columns in this order", "-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 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- (unavailable) elsewhere.

users is compatible with 4.3BSD.

who

Appears in the first edition of the UNIX Programmer's Manual as who(I):

who
who lists the name, typewriter channel, and login time for each current UNIX user.
This corresponds to all data in /tmp/utmp.

Version 2 AT&T UNIX, with the advent of wtmp(V), sees

who [ who-file ]
Without who-file, the output format is
# who
root    tty8 Jan  1 00:00:15
and all-zero-name (dead) entries are filtered out; with who-file the format becomes
# who /usr/adm/wtmp
        x Jan  1 00:02:02
root    8 Jan  1 00:02:11
        8 Jan  1 00:03:32
Usernames are 8 bytes, the teletype is just the bottom byte as a character (corresponding to the /dev/tty suffix) (so doing the same on utmp(V) would write the null byte instead), x indicates system reboot, as written by 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 ]
who-file (now /etc/utmp by default) excludes 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 , 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) ".

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 -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).

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 ≥ ).

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]
(with -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 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 /dev/ut_line write-only (and with O_NDELAY): if that (or closing) times out (after 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 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 users (more segfaults), which are then bubble-sorted, broken at 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 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 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.

X/Open Portability Guide Issue 2 (“XPG2”) specifies

who
who [ options ] [ file ]
who am i
who am I
all marked OF ("Output format incompletely specified"), the second additionally marked UN ("Possibly unsupportable feature"), loosely describing "the general format for output entries" as
name [state] line time activity pid [comment] [exit]
"[e]xcept for the default -s option", and listing
the user's name, terminal line, login time, elapsed time since activity occurred on the line and the process ID of the command interpreter for each current system user.
with am I identifying the invoking user.

writes "only the names and the number of users", ignoring all other options — this matches AT&T System V Release 4 UNIX;
"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;
"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;
adds "column headings";
"[t]urns on all options" — obviously wrong.
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"
"lists only those lines on which the system is waiting for someone to login" (sic!), requiring Name to be , and same format as for users sans the write status — this doesn't match AT&T System V Release 4 UNIX;
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;
, -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]
Listing "various pieces of information about accessible users" (who they are is implementation-defined); by default it's to output "in an unspecified format: the user's login name, terminal name, and time at which the user logged in", with -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

The historical 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
and speccing 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.

Appeared in 3BSD as users(1):

users - compact list of users who are on the system
listing "the login names of the users currently on the system in a compact, one-line format": that format being all non-empty login names (up to , segfaulting on overrun), sorted, all on one line, separated by a space. Despite being unmentioned in the SYNOPSIS, one argument, overriding the default /etc/utmp location, is allowed.

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 ) 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.

December 16, 2024 voreutils 5a9f9f29