3

Perl has access to some environment variables:

> echo $HOST
xtt006
> perl -E 'say $ENV{HOST}'
xtt006
> perl -E 'say `echo \$HOST`'
xtt006

But apparently there's another class of environment variables that aren't in the ENV hash:

> echo $env
opsd
> perl -E 'say $ENV{env}'
(no response from Perl)
> perl -E 'say `echo \$env`'
(no response from Perl)

What's up with that? Is there another technique by which Perl can obtain the value of $env?

Tanktalus
  • 21,664
  • 5
  • 41
  • 68
  • No, I don't see it when I run "env". But I do see it with "echo $env". – user3358338 Apr 18 '19 at 21:19
  • Yes, I should. I was told "you display the value of environment variable 'foo' by typing 'echo $foo', and apparently that was too simplistic an explanation. Is env a "local shell variable," as zdim indicates below? – user3358338 Apr 18 '19 at 22:50
  • "_too simplistic ..._" --- indeed, `echo $foo` displays the value of _any_ shell variable, local or environment. But `env` (or `printenv`) shows only environment variables (all of them). – zdim Apr 28 '19 at 05:55

3 Answers3

3

Try exporting your variable from the shell. This will move it from the shell's variable list to the environment that all subprocesses will be able to see.

export env
perl -E 'say $ENV{env}'

This should get it to show up.

Tanktalus
  • 21,664
  • 5
  • 41
  • 68
  • In bash, `printenv` will list all exported environment variables (that would be inherited by a child process), and `set` will list all variables including local environment variables in your shell. – mob Apr 18 '19 at 19:51
  • We use csh here, and I got "export: Command not found." /bin/env shows most (but not all) of my environment variables. "echo $env" returns opsd, while "env | grep env" returns nothing. – user3358338 Apr 18 '19 at 21:04
3

From %ENV in perlvar   (my emphasis)

The hash %ENV contains your current environment.

The environment is inherited from the process that started the Perl process, here the shell.

However, shell variables can be local, which aren't passed to child processes, or global, that form the environment and are inherited by child processes. The global ones are mostly read from configuration files at startup, but if they are to be added later then they need be exported

A variable created like the ones in the example above is only available to the current shell. It is a local variable: child processes of the current shell will not be aware of this variable. In order to pass variables to a subshell, we need to export them using the export built-in command. Variables that are exported are referred to as environment variables. Setting and exporting is usually done in one step:

export VARNAME="value" 

(my emphasis)   By your description it seems that $env is added as a local variable (with set), which then also need be exported (export env) in order to be seen in the one-liner.

Environment variables can be listed with env or printenv, while set lists all variables, both local and environment ones.


The above refers to the bash shell. The notion of "environment" is the same in other shells, and the same reasoning applies there as well; for a variable to be seen in child processes we need to make it global ("environment variable"). The difference is in how to do this.

In csh/tcsh use

setenv env "value"

instead of set var = "value", what would create a local variable.


Apart from using a global ("environment") variable instead of a local one, one can pass the variable to Perl code. See this post and this post for examples of doing that with one-liners. If an actual script is invoked then make it take arguments; the standard, and nicest, way is with Getopt::Long,

This isn't a special requirement on the users; they use your script and need to invoke it suitably.

zdim
  • 64,580
  • 5
  • 52
  • 81
  • @user3358338 Updated in relation to a shell other than `bash` – zdim Apr 18 '19 at 21:11
  • Is there a csh equivalent to "export"? Even if there is, it's not a very good solution... I wouldn't want users of my Perl script to have to "export env" before they run the script. – user3358338 Apr 18 '19 at 21:16
  • @user3358338 No (not that I know of) -- in csh/tcsh it's either `set` or `setenv`. As for what the users would do ... it they expect your script to know about a variable, they must either make it global or pass it to the script. Can you explain a little more? – zdim Apr 18 '19 at 21:18
  • I'm not the person who gave $env the value of opsd. I just log in and find it in that condition. I don't doubt that it was done with `set env = "opsd"`, as you say. I would like my Perl script to be able to make use of that value. I find it strange that csh has access to that value (with `echo $env`), but there's no way that Perl, launched from the csh that has access to that value, can have access to that value. – user3358338 Apr 19 '19 at 00:41
  • @user3358338 Yeah, got that. But `set env =...` creates a _local_ variable which just isn't seen by processes forked from that script. That's how it is. One can: (1) make it global instead (2) Call the script and pass it the value. This, I think, makes perfect sense. They call your script and they invoke it as appropriate. – zdim Apr 19 '19 at 07:07
  • @user3358338 Added a comment, and a few relevant links, to the end. – zdim Apr 19 '19 at 07:17
2

Just like Perl has private variables (my $x) and environment variables ($ENV{Y}), so does the shell.

$ perl <<'.'
my $x = "x";
$ENV{Y}="Y";
system 'echo "<$x> <$Y>"';
.
<> <Y>

$ sh <<'.'
x=x
export Y=Y
perl -le'print "<$x> <$ENV{Y}>";'
.
<> <Y>

$ csh <<'.'
set x x
setenv Y Y
perl -le'print "<$x> <$ENV{Y}>";'
.
<> <Y>

Having support for private variables is a good thing, not a strange thing.

In your example, $HOST is an environment variable (uppercase by convention), while $env is a private variable (lowercase by convention).


sh and bash

These shells use the same mechanism to assign to private variables and environment variables.

To create an environment variable, one promotes a private variable to an environment using export.

VAR=val
export VAR
perl -e'...$ENV{VAR}...'

or

export VAR
VAR=val
perl -e'...$ENV{VAR}...'

The promotion and assignment can be combined.

export VAR=val
perl -e'...$ENV{VAR}...'

The above approaches change the environment for the shell, and all subsequent children it creates. The following approach can be used to change a specific child's environment:

VAR=val perl -e'...$ENV{VAR}...'

That approach can also be used to effectively promote a private variable:

var=val
VAR="$var" perl -e'...$ENV{VAR}...'

csh and tcsh

These shells use different syntax for setting private variables and environment variables.

To set a private variable, one uses set.

set var val

To set an environment variable, one uses setenv.

setenv VAR val
perl -e'...$ENV{VAR}...'

The above approaches change the environment for the shell, and all subsequent children it creates.

ikegami
  • 367,544
  • 15
  • 269
  • 518