What is the difference between the two commands env and printenv? They both show the environment variables, and the output is exactly the same aside from _.

Are there any historical reasons for there being two commands instead of one?


Are there any historical reasons for there being two commands instead of one?

There was just history manner.

  1. Bill Joy wrote the first version of printenv command in 1979 for BSD.
  2. UNIX System III introduced env command in 1980.
  3. GNU followed UNIX System's env in 1986.
  4. BSD followed GNU/UNIX System's env in 1988.
  5. MINIX followed BSD's printenv in 1988.
  6. GNU followed MINX/BSD's printenv in 1989.
  7. GNU shell programming utilities 1.0 included printenv and env in 1991.
  8. GNU Shell Utilities merged into GNU coreutils in 2002, which was what's you could easily found in GNU/Linux nowadays.

Note that the "followed" doesn't means the source code was same, probably they were rewritten to avoid license lawsuit.

So the reason of why both commands existed is because when Bill Joy wrote printenv that time, the env doesn't exist yet. After 10 years of merging/compatibility and GNU come across it, you are now seeing both similar commands at the same page.

This history indicated as follows: (I try to minimized the answer and so only provides 2 essential source code here, the rest you can click attached links to see)

[fall of 1975]

Also arriving in the fall of 1975 were two unnoticed graduate students, Bill Joy and Chuck Haley; they both took an immediate interest in the new system. Initially they began working on a Pascal system that Thompson had hacked together while hanging around the 11/70 machine room.


Joy started compiling the first Berkeley Software Distribution (1BSD), which was released on March 9, 1978. //rf: https://en.wikipedia.org/wiki/Berkeley_Software_Distribution

[February, 1979]

1979(see "Bill Joy, UCB February, 1979") /1980(see "copyright[] =") , printenv.c //rf: http://minnie.tuhs.org/cgi-bin/utree.pl?file=2.11BSD/src/ucb/printenv.c

 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)printenv.c  5.1 (Berkeley) 5/31/85";
#endif not lint

 * printenv
 * Bill Joy, UCB
 * February, 1979

extern  char **environ;

main(argc, argv)
    int argc;
    char *argv[];
    register char **ep;
    int found = 0;

    argc--, argv++;
    if (environ)
        for (ep = environ; *ep; ep++)
            if (argc == 0 || prefix(argv[0], *ep)) {
                register char *cp = *ep;

                if (argc) {
                    while (*cp && *cp != '=')
                    if (*cp == '=')
                printf("%s\n", cp);
    exit (!found);

prefix(cp, dp)
    char *cp, *dp;

    while (*cp && *dp && *cp == *dp)
        cp++, dp++;
    if (*cp == 0)
        return (*dp == '=');
    return (0);


Hard to determine released in 2BSD OR 3BSD //rf: https://en.wikipedia.org/wiki/Berkeley_Software_Distribution

[June, 1980]

UNIX Release 3.0 OR "UNIX System III" //rf: ftp://pdp11.org.ru/pub/unix-archive/PDP-11/Distributions/usdl/SysIII/

[xiaobai@xiaobai pdp11v3]$ sudo grep -rni printenv . //no such printenv exist.
[xiaobai@xiaobai pdp11v3]$ sudo find . -iname '*env*'
[xiaobai@xiaobai pdp11v3]$ man ./sys3/usr/src/man/man1/env.1 | cat //but got env already
ENV(1)                                                                General Commands Manual                                                                ENV(1)

       env - set environment for command execution

       env [-] [ name=value ] ...  [ command args ]

       Env obtains the current environment, modifies it according to its arguments, then executes the command with the modified environment.  Arguments of the form
       name=value are merged into the inherited environment before the command is executed.  The - flag causes the inherited environment to be ignored  completely,
       so that the command is executed with exactly the environment specified by the arguments.

       If no command is specified, the resulting environment is printed, one name-value pair per line.

       sh(1), exec(2), profile(5), environ(7).

[xiaobai@xiaobai pdp11v3]$ 
[xiaobai@xiaobai pdp11v3]$ cat ./sys3/usr/src/cmd/env.c //diff with http://minnie.tuhs.org/cgi-bin/utree.pl?file=pdp11v/usr/src/cmd/env.c version 1.4, you will know this file is slightly older, so we can concluded that this file is "env.c version < 1.4"
 *      env [ - ] [ name=value ]... [command arg...]
 *      set environment, then execute command (or print environment)
 *      - says start fresh, otherwise merge with inherited environment
#include <stdio.h>

#define NENV    100
char    *newenv[NENV];
char    *nullp = NULL;

extern  char **environ;
extern  errno;
extern  char *sys_errlist[];
char    *nvmatch(), *strchr();

main(argc, argv, envp)
register char **argv, **envp;

        if (argc && strcmp(*argv, "-") == 0) {
                envp = &nullp;

        for (; *envp != NULL; envp++)
                if (strchr(*envp, '=') != NULL)
        while (*argv != NULL && strchr(*argv, '=') != NULL)

        if (*argv == NULL)
        else {
                environ = newenv;
                execvp(*argv, argv);
                fprintf(stderr, "%s: %s\n", sys_errlist[errno], *argv);

register char *arg;
        register char **p;

        for (p = newenv; *p != NULL && p < &newenv[NENV-1]; p++)
                if (nvmatch(arg, *p) != NULL) {
                        *p = arg;
        if (p >= &newenv[NENV-1]) {
                fprintf(stderr, "too many values in environment\n");
        *p = arg;

        register char **p = newenv;

        while (*p != NULL)
                printf("%s\n", *p++);

 *      s1 is either name, or name=value
 *      s2 is name=value
 *      if names match, return value of s2, else NULL

static char *
nvmatch(s1, s2)
register char *s1, *s2;

        while (*s1 == *s2++)
                if (*s1++ == '=')
        if (*s1 == '\0' && *(s2-1) == '=')
[xiaobai@xiaobai pdp11v3]$


BSD first printenv manual //rf: http://minnie.tuhs.org/cgi-bin/utree.pl?file=2.11BSD/src/man/man1/printenv.1 but i can't found manual related to env, the closest is getenv and environ //http://minnie.tuhs.org/cgi-bin/utree.pl?file=2.11BSD/src/man


First version of GNU env //rf: ftp://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/i386/1.0-RELEASE/ports/shellutils/src/env.c


MINIX 1st released //rf: https://en.wikipedia.org/wiki/Andrew_S._Tanenbaum

  • Tanenbaum wrote a clone of UNIX, called MINIX (MINi-unIX), for the IBM PC. It was targeted at students and others who wanted to learn how an operating system worked.


BSD 1st env.c //http://minnie.tuhs.org/cgi-bin/utree.pl?file=2.11BSD/src/usr.sbin/cron/env.c

/* Copyright 1988,1990,1993,1994 by Paul Vixie
 * All rights reserved

[October 4, 1988]

MINIX version 1.3 //rf: https://groups.google.com/forum/#!topic/comp.os.minix/cQ8kaiq1hgI

... 32932 190 /minix/commands/printenv.c //printenv.c already exist

//rf: http://www.informatica.co.cr/linux/research/1990/0202.htm


First version of GNU printenv, refer to [August 12, 1993].

[July 16, 1991]

"Shellutils" - GNU shell programming utilities 1.0 released //rf: https://groups.google.com/forum/#!topic/gnu.announce/xpTRtuFpNQc

The programs in this package are:

basename date dirname env expr groups id logname pathchk printenv printf sleep tee tty whoami yes nice nohup stty uname

[August 12, 1993]

printenv.c //rf: ftp://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/i386/1.0-RELEASE/ports/shellutils/src/printenv.c

, GNU Shell Utilities 1.8 //rf: ftp://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/i386/1.0-RELEASE/ports/shellutils/VERSION

/* printenv -- print all or part of environment
   Copyright (C) 1989, 1991 Free Software Foundation.


printenv.c which found on DSLinux source code in 2006 //rf: (Google) cache:mailman.dslinux.in-berlin.de/pipermail/dslinux-commit-dslinux.in-berlin.de/2006-August/000578.html

--- NEW FILE: printenv.c ---
 * Copyright (c) 1993 by David I. Bell

[November 1993]

The first version of FreeBSD was released. //rf: https://en.wikipedia.org/wiki/FreeBSD

[september 1, 2002]


The GNU fileutils, textutils, and sh-utils(see "Shellutils" at July 16, 1991 above) packages have been merged into one, called the GNU coreutils.

Overall, env use cases compare with printenv:

  1. print environment variables, but printenv can do the same
  2. Disable shell builtin but can achieve with enable cmd too.
  3. set variable but pointless due to some shells already can do it without env, e.g.

    $ HOME=/dev HOME=/tmp USER=root /bin/bash -c "cd ~; pwd"


  4. #!/usr/bin/env python header, but still not portable if env not in /usr/bin

  5. env -i, disable all env. I found it useful to figure out the critical environment variables for certain program to make it run from crontab. e.g. [1] In interactive mode, run declare -p > /tmp/d.sh to stores attributes variables. [2] In /tmp/test.sh, write: . /tmp/d.sh; eog /home/xiaobai/Pictures/1.jpg [3] Now run env -i bash /tmp/test.sh [4] If it success to display image, remove half of variables in /tmp/d.sh and run env -i bash /tmp/test.sh again. If something failed, undo it. Repeat the step to narrow down. [5] Finally I figure out eog requires $DISPLAY to run in crontab, and absent of $DBUS_SESSION_BUS_ADDRESS will slow down the display of image.

  6. target_PATH="$PATH:$(sudo printenv PATH)"; is useful to direct use the root path without have to further parsing the output of env or printenv.


xb@dnxb:~$ sudo env | grep PATH
xb@dnxb:~$ sudo printenv | grep PATH
xb@dnxb:~$ sudo printenv PATH
xb@dnxb:~$ sudo env PATH
env: ‘PATH’: No such file or directory
| improve this answer | |
  • 7
    Nice history lesson. – Ouki May 19 '16 at 7:08
  • Would be interesting to include some of the history of unix shell interpreters, where they have added syntax to make the env command redundant? – benjimin Nov 9 at 0:44

Having a different point of view (from FreeBSD), you have:

From man env:

 The env utility executes another utility after modifying the environment
 as specified on the command line.  Each name=value option specifies the
 setting of an environment variable, name, with a value of value.  All
 such environment variables are set before the utility is executed.
 If no utility is specified, env prints out the names and values of the
 variables in the environment, with one name/value pair per line.

From man printenv:

 The printenv utility prints out the names and values of the variables in
 the environment, with one name/value pair per line.  If name is speci-
 fied, only its value is printed.

So these commands might have the same effect without argument, but printenv sole purpose is to display the current environment key/values while env goal it to set some environment before calling another binary/script/whatever.

Is it more clear this way ?

To known more:

| improve this answer | |
  • 2
    From the provided links: The env command appeared in 4.4BSD. The -P, -S and -v options were added in FreeBSD 6.0. The printenv command appeared in 3.0BSD. So the historical reason seems to be that printenv arrived first. – mcmlxxxvi Oct 21 '15 at 8:51

env is POSIX 7, printenv is not (GNU Coreutils in Ubuntu 15.10).

| improve this answer | |

From man-pages:

env - run a program in a modified environment


printenv - print all or part of environment

Should be pretty explanatory.

| improve this answer | |
  • 4
    but i don't get it... – mikeserv Apr 7 '14 at 5:51
  • I suppose env comes before printenv. So why make another binary? This is not the same with what 'll' to 'ls' since 'll' is not a binary and do not have a man page. – WiSaGaN Apr 7 '14 at 5:55
  • @mikeserv printenv just prints all variables of current environment. With env you can prepare the same environment with some modifications if necessary and run an app in it. – UVV Apr 7 '14 at 6:51
  • @WiSaGaN your comparison is not really correct. ls is a binary, but ll is a common alias, which usually just expands to ls -l. printenv and env are two different binaries, I'm not sure which one was introduced first though. You can see some more examples here gnu.org/software/coreutils/manual/html_node/env-invocation.html – UVV Apr 7 '14 at 6:56
  • 1
    @mikeserv, see the mouseover text on this comic. :) – Wildcard Jan 7 '16 at 18:17

Talking strictly about functionalities, env is a binary with a huge set of features, one of them being printing environment variables, whereas printenv just prints environment variables.

Summarizing, if you are used to work with env, you will go with env for printing them (because that's what you are used to) and if you are not, you will typically remember printenv faster.

There are practically no differences when talking about printenv vs env for just printing environmental variables. I just checked and env is slightly heavier (about 5 extra KB), and their performance (in time) seems to be exactly the same.

Hope this clears it out! :)

| improve this answer | |

If you really want to know how different output is of the two binaries regardless of the history and legacy of the them, you can run a few utilities to gauge this difference. On debian I ran a few things that will be different depending on any custom environmental vars:

env |wc -l
printenv |wc -l

Both my output has 41 lines

env > env.txt
printenv > printenv.txt
diff env.txt printenv.txt


< _=/usr/bin/env
> _=/usr/bin/printenv

So you see there is one line different between the two and that line is number 41 which I guess stipulates the binary used in the command. Without additional arguments, these report virtual identical info for me.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.