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
and env
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
[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 same
Disable 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 if env
not in /usr/bin
env -i
, disable all env. I find 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.
target_PATH="$PATH:$(sudo printenv PATH)";
is useful to directly use the root path without having to further parse the output of env
or printenv
.
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:~$
Locale settings are user preferences that relate to your culture.
Locale names
On all current unix variants that I know of (but not on a few antiques), locale names follow the same pattern:
- An ISO 639-1 lowercase two-letter language code, or an ISO 639-2 three-letter language code if the language has no two-letter code. For example,
en
for English, de
for German, ja
for Japanese, uk
for Ukrainian, ber
for Berber, …
- For many but not all languages, an underscore
_
followed by an ISO 3166 uppercase two-letter country code. Thus: en_US
for US English, en_UK
for British English, fr_CA
Canadian (Québec) French, de_DE
for German of Germany, de_AT
for German of Austria, ja_JP
for Japanese (of Japan), etc.
- Optionally, a dot
.
followed by the name of a character encoding such as UTF-8
, ISO-8859-1
, KOI8-U
, GB2312
, Big5
, etc. With GNU libc at least (I don't know how widespread this is), case and punctuation is ignored in encoding names. For example, zh_CN.UTF-8
is Mandarin (simplified) Chinese encoded in UTF-8, while zh_CN
is Mandarin Chinese encoded in GB2312, and zh_TW
is Taiwanese (traditional) Chinese encoded in Big5.
- Optionally, an at sign
@
followed by the name of a variant. The meaning of variants is locale-dependent. For example, many European countries have an @euro
locale variant where the currency sign is € and where the encoding is one that includes this character (ISO 8859-15 or ISO 8859-16), as opposed to the unadorned variant with the older currency sign. For example, en_IE
(English, Ireland) uses the latin1 (ISO 8859-1) encoding and £ as the currency symbol while en_IE@euro
uses the latin9 (ISO 8859-15) encoding and € as the currency symbol.
In addition, there are two locale names that exist on all unix-like system: C
and POSIX
. These names are synonymous and mean computerese, i.e. default settings that are appropriate for data that is parsed by a computer program.
Locale settings
The following locale categories are defined by POSIX:
LC_CTYPE
: the character set used by terminal applications: classification data (which characters are letters, punctuation, spaces, invalid, etc.) and case conversion. Text utilities typically heed LC_CTYPE
to determine character boundaries.
LC_COLLATE
: collation (i.e. sorting) order. This setting is of very limited use for several reasons:
- Most languages have intricate rules that depend on what is being sorted (e.g. dictionary words and proper names might not use the same order) and cannot be expressed by
LC_COLLATE
.
- There are few applications where proper sort order matters which are performed by software that uses locale settings. For example, word processors store the language and encoding of a file in the file itself (otherwise the file wouldn't be processed correctly on a system with different locale settings) and don't care about the locale settings specified by the environment.
LC_COLLATE
can have nasty side effects, in particular because it causes the sort order A < a < B < …, which makes “between A and Z” include the lowercase letters a through y. In particular, very common regular expressions like [A-Z]
break some applications.
LC_MESSAGES
: the language of informational and error messages.
LC_NUMERIC
: number formatting: decimal and thousands separator.
Many applications hard-code .
as a decimal separator. This makes LC_NUMERIC
not very useful and potentially dangerous:
- Even if you set it, you'll still see the default format pretty often.
- You're likely to get into a situation where one application produces locale-dependent output and another application expects
.
to be the decimal point, or ,
to be a field separator.
LC_MONETARY
: like LC_NUMERIC
, but for amounts of local currency.
Very few applications use this.
LC_TIME
: date and time formatting: weekday and month names, 12 or 24-hour clock, order of date parts, punctuation, etc.
GNU libc, which you'll find on non-embedded Linux, defines additional locale categories:
LC_PAPER
: the default paper size (defined by height and width).
LC_NAME
, LC_ADDRESS
, LC_TELEPHONE
, LC_MEASUREMENT
, LC_IDENTIFICATION
: I don't know of any application that uses these.
Environment variables
Applications that use locale settings determine them from environment variables.
- Then the value of the
LANG
environment variable is used unless overridden by another setting. If LANG
is not set, the default locale is C
.
- The
LC_xxx
names can be used as environment variables.
- If
LC_ALL
is set, then all other values are ignored; this is primarily useful to set LC_ALL=C
run applications that need to produce the same output regardless of where they are run.
- In addition, GNU libc uses
LANGUAGE
to define fallbacks for LC_MESSAGES
(e.g. LANGUAGE=fr_BE:fr_FR:en
to prefer Belgian French, or if unavailable France French, or if unavailable English).
Installing locales
Locale data can be large, so some distributions don't ship them in a usable form and instead require an additional installation step.
- On Debian, to install locales, run
dpkg-reconfigure locales
and select from the list in the dialog box, or edit /etc/locale.gen
and then run locale-gen
.
- On Ubuntu, to install locales, run
locale-gen
with the names of the locales as arguments.
You can define your own locale.
Recommendation
The useful settings are:
- Set
LC_CTYPE
to the language and encoding that you encode your text files in. Ensure that your terminals use that encoding.
For most languages, only the encoding matters. There are a few exceptions; for example, an uppercase i
is I
in most languages but İ
in Turkish (tr_TR
).
- Set
LC_MESSAGES
to the language that you want to see messages in.
- Set
LC_PAPER
to en_US
if you want US Letter to be the default paper size and just about anything else (e.g. en_GB
) if you want A4.
- Optionally, set
LC_TIME
to your favorite time format.
As explained above, avoid setting LC_COLLATE
and LC_NUMERIC
. If you use LANG
, explicitly override these two categories by setting them to C
.
Best Answer
The
locale
command does not print environment variables. It prints the state of the user's locale, whether set by environment or by inference.For example, if
LC_TIME
is not set, its value is taken fromLANG
. And ifLC_ALL
is set, all other settings are overriden.Try it:
You will observe that computed values are shown in double quotes and those that are explicitly set by environment variables are unquoted.