What is the difference between 'env' and 'printenv'?
Are there any historical reasons for there being two commands instead of one?
There was, just history manner.
- Bill Joy wrote the first version of
printenv
command in 1979 for BSD. - UNIX System III introduced
env
command in 1980. - GNU followed UNIX System's
env
in 1986. - BSD followed GNU/UNIX System's
env
in 1988. - MINIX followed BSD's
printenv
in 1988. - GNU followed MINX/BSD's
printenv
in 1989. - GNU shell programming utilities 1.0 included
printenv
andenv
in 1991. - GNU Shell Utilities merged into GNU coreutils in 2002, which is what you find in GNU/Linux nowadays.
Note that the "followed" doesn't means the source code was the same, probably they were rewritten to avoid license lawsuits.
So, the reason why both commands existed is because Bill Joy wrote printenv
even before env
existed.
After 10 years of merging/compatibility and GNU come across it, you are now seeing both similar commands on the same page.
This history indicated as follows: (I tried to minimize the answer and only provided 2 essential source code snippets here. The rest you can click the attached links to see more)
[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.
[1977]
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;
found++;
if (argc) {
while (*cp && *cp != '=')
cp++;
if (*cp == '=')
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);
}
[1979]
Hard to determine released in 2BSD OR 3BSD //rf: https://en.wikipedia.org/wiki/Berkeley_Software_Distribution
3BSD The printenv command appeared in 3.0 BSD. //rf: http://www.freebsd.org/cgi/man.cgi?query=printenv&sektion=1#end 3.0 BSD introduced at 1979 //rf: http://gunkies.org/wiki/3_BSD
2BSD The printenv command first appeared in 2BSD //rf: http://man.openbsd.org/printenv.1
[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*'
./sys3/usr/src/lib/libF77/getenv_.c
./sys3/usr/src/lib/libc/vax/gen/getenv.c
./sys3/usr/src/lib/libc/pdp11/gen/getenv.c
./sys3/usr/src/man/man3/getenv.3c
./sys3/usr/src/man/docs/c_env
./sys3/usr/src/man/docs/mm_man/s03envir
./sys3/usr/src/man/man7/environ.7
./sys3/usr/src/man/man1/env.1
./sys3/usr/src/cmd/env.c
./sys3/bin/env
[xiaobai@xiaobai pdp11v3]$ man ./sys3/usr/src/man/man1/env.1 | cat //but got env already
ENV(1) General Commands Manual ENV(1)
NAME
env - set environment for command execution
SYNOPSIS
env [-] [ name=value ] ... [ command args ]
DESCRIPTION
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.
SEE ALSO
sh(1), exec(2), profile(5), environ(7).
ENV(1)
[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;
{
argc--;
argv++;
if (argc && strcmp(*argv, "-") == 0) {
envp = &nullp;
argc--;
argv++;
}
for (; *envp != NULL; envp++)
if (strchr(*envp, '=') != NULL)
addname(*envp);
while (*argv != NULL && strchr(*argv, '=') != NULL)
addname(*argv++);
if (*argv == NULL)
print();
else {
environ = newenv;
execvp(*argv, argv);
fprintf(stderr, "%s: %s\n", sys_errlist[errno], *argv);
exit(1);
}
}
addname(arg)
register char *arg;
{
register char **p;
for (p = newenv; *p != NULL && p < &newenv[NENV-1]; p++)
if (nvmatch(arg, *p) != NULL) {
*p = arg;
return;
}
if (p >= &newenv[NENV-1]) {
fprintf(stderr, "too many values in environment\n");
print();
exit(1);
}
*p = arg;
return;
}
print()
{
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++ == '=')
return(s2);
if (*s1 == '\0' && *(s2-1) == '=')
return(s2);
return(NULL);
}
[xiaobai@xiaobai pdp11v3]$
[1985]
BSD first printenv manual //rf: http://minnie.tuhs.org/cgi-bin/utree.pl?file=2.11BSD/src/man/man1/printenv.1
I couldn't find the manual related to env, but the closest is getenv and environ //http://minnie.tuhs.org/cgi-bin/utree.pl?file=2.11BSD/src/man
[1986]
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
[1987]
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.
[1988]
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
[1989]
The 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.
...
[1993]
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]
http://git.savannah.gnu.org/cgit/coreutils.git/tree/README-package-renamed-to-coreutils
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
:
print environment variables, but
printenv
can do the sameDisable shell builtin but can achieve with
enable
cmd too.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"
/tmp
#!/usr/bin/env python
header, but still not portable ifenv
not in /usr/binenv -i
, disable all env. I find it useful to figure out the critical environment variables for certain program, to make it run fromcrontab
. e.g. [1] In interactive mode, rundeclare -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 runenv -i bash /tmp/test.sh
[4] If it success to display image, remove half of variables in/tmp/d.sh
and runenv -i bash /tmp/test.sh
again. If something failed, undo it. Repeat the step to narrow down. [5] Finally I figure outeog
requires$DISPLAY
to run incrontab
, and absent of$DBUS_SESSION_BUS_ADDRESS
will slow down the display of image.target_PATH="$PATH:$(sudo printenv PATH)";
is useful to directly use the root path without having to further parse the output ofenv
orprintenv
.
e.g:
xb@dnxb:~$ sudo env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
xb@dnxb:~$ sudo printenv | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
xb@dnxb:~$ sudo printenv PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
xb@dnxb:~$ sudo env PATH
env: ‘PATH’: No such file or directory
xb@dnxb:~$
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:
man 1 env
(FreeBSD)man 1 printenv
(FreeBSD)
env
is POSIX 7, printenv
is not (GNU Coreutils in Ubuntu 15.10).