I want to write and test scripts that do not depend on any of the personal environment variables accessible to me at the command line. For instance, each time I open a new command line window on my macbook pro, $PATH is updated by having my personal bin directory prepended to it --- as well as several other directories, such as

/usr/sbin:/sbin:/Library/TeX/texbin:/Library/Apple/usr/bin

This occurs apparently because my $HOME/.profile is automatically sourced when a window opens up. In addition, a large number of other variables are defined at login because my personal .bashrc is sourced automatically.

Ultimately, I want to be able to run a script as a cron job --- using crontab -l. But if the script depends on my personal configuration, or if it tries to access a script that lives in my personal bin, it will halt with error.

So I write a script, test it at the command line, set up a cron job to run it; and when the time comes, it halts with error, because it turns out that the script depended on one of my personal environment variables.

Currently, then, the only way I know to test such a script is to set up a cron job --- say, five minutes from now---and then wait for the cron job to run. It's clumsy and slow.

Is there a direct and immediate way to run a script so that it does not know anything from $HOME/.profile?

The script I tested this with:

#!/usr/bin/perl
use strict; use warnings;
use Data::Dumper qw(Dumper);
$Data::Dumper::Sortkeys = 1;
print Dumper {%ENV};

The following command

> jaw20210419test.pl | wc
    2532    3098  118462

produces many lines of output, as you see, because of all that I define in my .bashrc. With the following,

> sudo jaw20210419test.pl | wc
Password:
      20      59     831
> 

the output is much less, but it still has an updated $PATH including my personal bin. I do not understand this, since the $PATH gets updated by $HOME/.profile, and that file sources my personal $HOME/u/kh/.bashrc.

If however I run this with a cron job, then it does not have an updated $PATH. The script says

$VAR1 = {
          'HOME' => '/Users/kpr',
          'LOGNAME' => 'kpr',
          'PATH' => '/usr/bin:/bin',
          'PWD' => '/Users/kpr',
          'SHELL' => '/bin/sh',
          'SHLVL' => '1',
          'USER' => 'kpr',
          'VERSIONER_PERL_VERSION' => '5.18',
          '_' => '/Users/kpr/u/kh/bin/jaw20210419test.pl',
          '__CF_USER_TEXT_ENCODING' => '0x1F5:0x0:0x0'
        };

This is the configuration I want to test the script with. Note that it knows who I am --- $USER --- and where $HOME is.

By contrast, two other proposed solutions, from How to start a shell without any user configuration?, do not even know know where $HOME is:

> echo $cdbin # personal environment variable
/Users/kpr/u/kh/bin
> env -i perl $cdbin/jaw20210419test.pl 
$VAR1 = {
          'VERSIONER_PERL_VERSION' => '5.18',
          '__CF_USER_TEXT_ENCODING' => '0x1F5:0x0:0x0'
        }; 
> env --noprofile --norc  perl $cdbin/jaw20210419test.pl 
env: illegal option -- n
usage: env [-iv] [-P utilpath] [-S string] [-u name]
           [name=value ...] [utility [argument ...]]
> echo $SHELL
/bin/bash
> 
> env -i sh -c  $cdbin/jaw20210419test.pl 
$VAR1 = {
          'PWD' => '/Users/kpr/u/sng/2021/FR-Wegelin-TO-stackoverflow',
          'SHLVL' => '1',
          'VERSIONER_PERL_VERSION' => '5.18',
          '_' => '/Users/kpr/u/kh/bin/jaw20210419test.pl',
          '__CF_USER_TEXT_ENCODING' => '0x1F5:0x0:0x0'
        };

so I am still lost.

1

There are 1 answers

0
James Risner On

Of note, you can not technically do what you wish. Bash goes to great lengths to determine if you are running an interactive shell or a non-interactive shell. Running from cron will forever be non-interactive as is running from a terminal will forever be marked as interactive.

You want a number of entries not present in cron for your test shell, for example PS1 for a prompt. Also there are quite a few read only environment variables. If you try to unset them you get an error:

bash: unset: SHELLOPTS: cannot unset: readonly variable

You can get 95 percent there by this method:

% env -i bash --noprofile --norc
bash-3.2$ source be-cron.sh
bash-3.2$

The file be-cron.sh will "unset" most of the interactive environment variables:

#!/bin/bash
unset BASH
unset BASH_ENV
unset BASH_VERSION
unset COLORFGBG
unset COLORTERM
unset COLUMNS
unset COMMAND_MODE
unset DIRSTACK
unset DISPLAY
unset EDITOR
unset GROUP
unset GROUPS
unset HISTFILE
unset HISTFILESIZE
unset HISTSIZE
unset HOST
unset HOSTNAME
unset HOSTTYPE
unset IFS
unset ITERM_PROFILE
unset ITERM_SESSION_ID
unset LC_TERMINAL
unset LC_TERMINAL_VERSION
unset LINES
unset LaunchInstanceID
unset MACHTYPE
unset MAILCHECK
unset MANPATH
unset MANPATH_WITHOUT_PERLBREW
unset OPTERR
unset OPTIND
unset OSTYPE
unset PATH_WITHOUT_PERLBREW
unset PERLBREW_HOME
unset PERLBREW_PATH
unset PERLBREW_ROOT
unset PERLBREW_SHELLRC_VERSION
unset PIPESTATUS
unset PS2
unset PS4
unset SECURITYSESSIONID
unset SSH_AUTH_SOCK
unset TERM
unset TERM_PROGRAM
unset TERM_PROGRAM_VERSION
unset TERM_SESSION_ID
unset TMPDIR
unset VENDOR
unset XPC_FLAGS
unset XPC_SERVICE_NAME
unset __CFBundleIdentifier