Discussion:
the usage of env
(too old to reply)
p***@gmx.it
2024-07-19 12:20:01 UTC
Permalink
Hello list,

I am not sure how 'env' command works.

for example, what's the difference between '/usr/bin/perl' and 'env
perl' ?

I know env may set a environment variable in system, so my question also
includes:

1. where to see a shell environment variable? I tried 'echo $ENV'
showing nothing.

2. then I tried the following one-line perl to search for 'perl'
environment variable.

$ perl -le 'for( keys %ENV ){print "$_ --> $ENV{$_}"}' |grep perl
_ --> /usr/bin/perl

the key for perl is "_" in environment variable? under this key, why
'env perl' just works?

Thanks for your help.

regards.
timothy
Greg Wooledge
2024-07-19 12:20:01 UTC
Permalink
Post by p***@gmx.it
for example, what's the difference between '/usr/bin/perl' and 'env
perl' ?
"env perl" searches your $PATH.
Post by p***@gmx.it
I know env may set a environment variable in system, so my question also
env is used to *display* the current shell environment, or to set a new
environment for *one* specific process.

E.g.

env FOO=bar ./myprogram

This launches ./myprogram with the additional environment variable FOO
set to the value bar.
Post by p***@gmx.it
1. where to see a shell environment variable? I tried 'echo $ENV'
showing nothing.
Just run env with no arguments.
Post by p***@gmx.it
the key for perl is "_" in environment variable? under this key, why
'env perl' just works?
The _ environment variable is weird. Please ignore it for now. You
can live a full and happy life without ever worrying about it.
Thomas Schmitt
2024-07-19 12:50:01 UTC
Permalink
Hi,
Post by p***@gmx.it
I am not sure how 'env' command works.
Read the output of

man env
Post by p***@gmx.it
for example, what's the difference between '/usr/bin/perl' and 'env perl' ?
Reading the man page i'd say it's the same difference as between
"/usr/bin/perl" and "perl". I.e. the former runs explicitely a particular
program file, whereas the latter runs a program file which the shell
picks for you, depending on the setting of the PATH variable.

echo "$PATH"
Post by p***@gmx.it
I know env may set a environment variable in system,
Not "in system", but for the particular program run which gets started by
"env". "man env" says:

SYNOPSIS
env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]

DESCRIPTION
Set each NAME to VALUE in the environment and run COMMAND.

The NAME=VALUE pairs will be in effect only as long as "env" and the
started program run.


(In a bash shell, the main advantages over plain
[NAME=VALUE]... [COMMAND [ARG]...]
are probably the env option -i, which disables all inherited exported
variables, and -u which disables a particular inherited variable.)
Post by p***@gmx.it
1. where to see a shell environment variable? I tried 'echo $ENV'
showing nothing.
If you want to see all exported variables:

env

because the statement in "man env":

If no COMMAND, print the resulting environment.

Example shell session (with prompt "$"):

$ export x=X
$ y=Y
$ echo "$x"
X
$ echo "$y"
Y
$ env | grep '^[xy]='
x=X
$

The not exported variale "y" does not show up in env's output.
Post by p***@gmx.it
why 'env perl' just works?
Program "env" (or its helpers) finds a program file with name "perl"
in one of the directories which are listed in $PATH.


Have a nice day :)

Thomas
Michel Verdier
2024-07-19 13:10:01 UTC
Permalink
Post by p***@gmx.it
$ perl -le 'for( keys %ENV ){print "$_ --> $ENV{$_}"}' |grep perl
_ --> /usr/bin/perl
the key for perl is "_" in environment variable? under this key, why
'env perl' just works?
Perl $_ is the current (unnamed) value of your loop "for". You could
write it like this:
foreach my $key (keys %ENV) { print "$key=$ENV{$key}" }

https://perldoc.perl.org/variables
The Wanderer
2024-07-19 13:40:01 UTC
Permalink
Post by Michel Verdier
Post by p***@gmx.it
$ perl -le 'for( keys %ENV ){print "$_ --> $ENV{$_}"}' |grep perl
_ --> /usr/bin/perl
the key for perl is "_" in environment variable? under this key, why
'env perl' just works?
Perl $_ is the current (unnamed) value of your loop "for". You could
foreach my $key (keys %ENV) { print "$key=$ENV{$key}" }
https://perldoc.perl.org/variables
I think the question was not about the '$_' variable in the perl
expression, but about the '_' variable in the output, which in this case
contains the full path to the 'perl' binary.

$ env | grep ^_
_=/usr/bin/env

That appears to be a (shell?) environment variable. Greg commented on
it, as being a weird thing that you will probably never need to worry
about. I have done some experimenting with it, and it appears to hold
the equivalent of argv[0] for the command line which invoked the current
process - but the logic for determining what that is may well not be
intuitive for many people.

'echo $_' produces the full path to 'echo'.

'sh /tmp/testme' (where that file is a '#!/bin/sh' script which runs
'echo $_') produces the full path to 'sh'.

'bash /tmp/testme' (with the script shebang line still pointing to
/bin/sh) produces the full path to 'bash'.

'chmod +x /tmp/testme' followed by '/tmp/testme' produces '/tmp/testme'.


All in all, I think I agree with Greg's description.
--
The Wanderer

The reasonable man adapts himself to the world; the unreasonable one
persists in trying to adapt the world to himself. Therefore all
progress depends on the unreasonable man. -- George Bernard Shaw
Mike Castle
2024-07-19 16:10:01 UTC
Permalink
In addition to what everyone else has said about env(1), there is the
fact that Korn derived shells also supports some of the same features.

env VAR1=foo VAR2=bar random-command
VAR1=foo VAR2=bar random-command

If running a Korn-like shell (ksh, bash, zsh), both would set the
envvars VAR1 and VAR2 to those values. (Note: I don't *think* earlier
Bourne shells support that, but I may be misremembering when the
feature was introduced. It was likely before even my time.)

However, C-shell derived do not, you must use the env(1) command.

But, env(1) does also offers the -i and -u flags to initialize and
unset envvars. I use -u a lot during dev/testing to make sure scripts
I write work correctly in various combinations.

mrc
p***@gmx.it
2024-07-19 21:50:01 UTC
Permalink
Post by Mike Castle
In addition to what everyone else has said about env(1), there is the
fact that Korn derived shells also supports some of the same features.
env VAR1=foo VAR2=bar random-command
VAR1=foo VAR2=bar random-command
$ VAR1=foo && ./a.sh
$ export VAR2=foo; ./a.sh
$ ./b.sh


$VAR1 will be seen by a.sh only, but $VAR2 can be seen my current login
session (such as b.sh). Am I right? I am a bit confused about env scope.

Thanks for your kind help.
Greg Wooledge
2024-07-19 22:00:01 UTC
Permalink
Post by p***@gmx.it
$ VAR1=foo && ./a.sh
$ export VAR2=foo; ./a.sh
$ ./b.sh
$VAR1 will be seen by a.sh only, but $VAR2 can be seen my current login
session (such as b.sh). Am I right? I am a bit confused about env scope.
If we assume NO other commands have been executed in this shell so far,
then:

VAR1 is not marked for export. It's just a regular shell variable.
It won't be seen by either call to ./a.sh which is a (non-subshell)
child process, not will it be seen by ./b.sh which is also a non-subshell
child.

VAR2 is marked for export by the interactive shell. It's a permanent
part of the shell's environment and will be seen by all child processes
from that point forward.

VAR2 will therefore be seen by the second call to ./a.sh and the call
to ./b.sh.

Now, what you didn't ask, but what I *expected* you to ask, is:

What's the difference between these two commands?
VAR3=foo ./a.sh
VAR3=bar; ./a.sh

In the first command, VAR3 is placed in the environment of the command
being executed. ./a.sh will see it. VAR3 will not survive beyond
this command. It will be discarded, and future commands will not be
aware it ever existed.

In the second command, VAR3 is created as a regular variable in the
current shell, but not exported to the environment. It will NOT be
seen by ./a.sh, but it WILL be seen by future shell commands within
this session.
p***@gmx.it
2024-07-19 22:20:01 UTC
Permalink
Post by Greg Wooledge
Post by p***@gmx.it
$ VAR1=foo && ./a.sh
$ export VAR2=foo; ./a.sh
$ ./b.sh
$VAR1 will be seen by a.sh only, but $VAR2 can be seen my current login
session (such as b.sh). Am I right? I am a bit confused about env scope.
If we assume NO other commands have been executed in this shell so far,
VAR1 is not marked for export. It's just a regular shell variable.
It won't be seen by either call to ./a.sh which is a (non-subshell)
child process, not will it be seen by ./b.sh which is also a
non-subshell
child.
VAR2 is marked for export by the interactive shell. It's a permanent
part of the shell's environment and will be seen by all child processes
from that point forward.
VAR2 will therefore be seen by the second call to ./a.sh and the call
to ./b.sh.
I verified that as follows.

$ VAR=foo ./a.sh
i can see VAR=foo

$ ./a.sh
i can see VAR=

$ VAR=foo; ./a.sh
i can see VAR=

$ export VAR=foo; ./a.sh
i can see VAR=foo

Thanks Greg.
Post by Greg Wooledge
What's the difference between these two commands?
VAR3=foo ./a.sh
VAR3=bar; ./a.sh
In the first command, VAR3 is placed in the environment of the command
being executed. ./a.sh will see it. VAR3 will not survive beyond
this command. It will be discarded, and future commands will not be
aware it ever existed.
In the second command, VAR3 is created as a regular variable in the
current shell, but not exported to the environment. It will NOT be
seen by ./a.sh, but it WILL be seen by future shell commands within
this session.
I can not clearly understand for this statement. what's "future shell
commands"? can you show an example?

regards,
timothy
Greg Wooledge
2024-07-19 22:30:01 UTC
Permalink
Post by p***@gmx.it
$ VAR=foo ./a.sh
i can see VAR=foo
I don't know what "see" means here.

hobbit:~$ cat a.sh
#!/bin/sh
echo "I am a.sh, and inside me, VAR=<$VAR>."
hobbit:~$ unset -v VAR
hobbit:~$ VAR=foo ./a.sh
I am a.sh, and inside me, VAR=<foo>.
hobbit:~$ echo "VAR=<$VAR>"
VAR=<>

VAR is defined in the environment of a.sh but NOT in the calling shell.
Post by p***@gmx.it
Post by Greg Wooledge
What's the difference between these two commands?
VAR3=foo ./a.sh
VAR3=bar; ./a.sh
In the first command, VAR3 is placed in the environment of the command
being executed. ./a.sh will see it. VAR3 will not survive beyond
this command. It will be discarded, and future commands will not be
aware it ever existed.
In the second command, VAR3 is created as a regular variable in the
current shell, but not exported to the environment. It will NOT be
seen by ./a.sh, but it WILL be seen by future shell commands within
this session.
I can not clearly understand for this statement. what's "future shell
commands"? can you show an example?
hobbit:~$ unset -v VAR
hobbit:~$ VAR=bar; ./a.sh
I am a.sh, and inside me, VAR=<>.
hobbit:~$ echo "VAR=<$VAR>"
VAR=<bar>
Greg Wooledge
2024-07-19 22:40:02 UTC
Permalink
Post by Greg Wooledge
Post by p***@gmx.it
I can not clearly understand for this statement. what's "future shell
commands"? can you show an example?
hobbit:~$ unset -v VAR
hobbit:~$ VAR=bar; ./a.sh
I am a.sh, and inside me, VAR=<>.
hobbit:~$ echo "VAR=<$VAR>"
VAR=<bar>
OK I know that. $VAR can be seen by future shell command in this
session, but cannot be seen by a sub-shell such as a.sh.
a.sh is NOT a subshell. That's really super important.

a.sh is a NON-subshell child process.

In an actual subshell, you *would* see the variable, because subshells
inherit regular variables as well as environment variables.

hobbit:~$ unset -v VAR
hobbit:~$ VAR=foo; (echo "I am a subshell, and VAR=<$VAR>")
I am a subshell, and VAR=<foo>
hobbit:~$ ./a.sh
I am a.sh, and inside me, VAR=<>.

https://mywiki.wooledge.org/SubShell
p***@gmx.it
2024-07-19 22:50:01 UTC
Permalink
Post by Greg Wooledge
Post by Greg Wooledge
Post by p***@gmx.it
I can not clearly understand for this statement. what's "future shell
commands"? can you show an example?
hobbit:~$ unset -v VAR
hobbit:~$ VAR=bar; ./a.sh
I am a.sh, and inside me, VAR=<>.
hobbit:~$ echo "VAR=<$VAR>"
VAR=<bar>
OK I know that. $VAR can be seen by future shell command in this
session, but cannot be seen by a sub-shell such as a.sh.
a.sh is NOT a subshell. That's really super important.
a.sh is a NON-subshell child process.
In an actual subshell, you *would* see the variable, because subshells
inherit regular variables as well as environment variables.
hobbit:~$ unset -v VAR
hobbit:~$ VAR=foo; (echo "I am a subshell, and VAR=<$VAR>")
I am a subshell, and VAR=<foo>
hobbit:~$ ./a.sh
I am a.sh, and inside me, VAR=<>.
https://mywiki.wooledge.org/SubShell
make sense now. thanks a lot!
p***@gmx.it
2024-07-19 22:40:02 UTC
Permalink
Post by Greg Wooledge
Post by p***@gmx.it
I can not clearly understand for this statement. what's "future shell
commands"? can you show an example?
hobbit:~$ unset -v VAR
hobbit:~$ VAR=bar; ./a.sh
I am a.sh, and inside me, VAR=<>.
hobbit:~$ echo "VAR=<$VAR>"
VAR=<bar>
OK I know that. $VAR can be seen by future shell command in this
session, but cannot be seen by a sub-shell such as a.sh.

Thanks.
Max Nikulin
2024-07-20 04:00:01 UTC
Permalink
Post by The Wanderer
#!/bin/sh
echo "I am a.sh, and inside me, VAR=<$VAR>."
A way to report a bit more information:

cat /tmp/test.sh
#!/bin/sh
printf "%s: VAR %5s %10s value=<%s>\n" \
"$0" "${VAR+set}" "${VAR:+not empty}" "$VAR"

/tmp/test.sh
/tmp/test.sh: VAR value=<>

VAR= /tmp/test.sh
/tmp/test.sh: VAR set value=<>

VAR=a /tmp/test.sh
/tmp/test.sh: VAR set not empty value=<a>
Post by The Wanderer
$ env | grep ^_
_=/usr/bin/env
That appears to be a (shell?) environment variable.
<https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-_005f>
"Bash Variables"
Alternatively
info "(bash)Bash Variables"
man bash

Back to the original questions, "env" is handy to force path search,
e.g. in shebang

#!/usr/bin/env perl

unlike

#!/usr/bin/perl

may run perl from /usr/local/bin/perl, ~/bin/perl, etc. depending on
$PATH. There are some cases when commands are not interpreted by shell
and "env" allows to set environment variables for program, e.g. Exec=
entries in .desktop files allow

Exec=env VAR=value some-application

but not

Exec=VAR=value some-application

(I do not remember if VAR=value needs escaping) despite both cases work
in shell.

Loading...