1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
|
#!/usr/bin/env perl
## no critic (ControlStructures::ProhibitPostfixControls)
## no critic (ValuesAndExpressions::ProhibitConstantPragma)
use strict;
use warnings;
use open ':std', IO => ':encoding(UTF-8)';
# ABSTRACT: Read .env file and turn its content into environment variables for different shells.
# PODNAME: envdot
our $VERSION = '0.018';
use English qw( -no_match_vars ); # Avoids regex performance penalty in perl 5.18 and earlier
use Getopt::Long qw( :config auto_version auto_help );
use Carp;
use Errno;
use Pod::Usage;
use Env::Dot::Functions qw(:all);
use Env::Dot::ScriptFunctions qw( convert_variables_into_commands );
local $OUTPUT_AUTOFLUSH = 1;
use constant {
DEFAULT_OPTION_DOTENV_FILENAME => '.env',
DEFAULT_OPTION_SHELL => q{sh},
DEFAULT_OPTION_READ_FROM_STDIN => 0,
EXIT_SUCCESS => 0,
EXIT_ERROR_NO_FILE => ( exists &Errno::ENOENT ? Errno::ENOENT : 255 ),
EXIT_ERROR_OTHER_ERROR => ( exists &Errno::EINVAL ? Errno::EINVAL : 255 ),
};
my %SHELL_ALTERNATIVES = (
sh => 'sh',
bash => 'sh',
dash => 'sh',
ksh => 'sh',
csh => 'csh',
tcsh => 'csh',
fish => 'fish',
);
my $man = 0;
my $export = 1;
my $shell = $SHELL_ALTERNATIVES{ $ENV{SHELL} } // DEFAULT_OPTION_SHELL;
my $dotenv_filepath = DEFAULT_OPTION_DOTENV_FILENAME;
my $read_from_stdin = DEFAULT_OPTION_READ_FROM_STDIN;
GetOptions(
'man' => \$man,
'export!' => \$export,
'shell|s=s' => \$shell,
'dotenv|e=s' => \$dotenv_filepath,
'' => \$read_from_stdin, ## no critic (ValuesAndExpressions::ProhibitEmptyQuotes)
) or pod2usage(2);
pod2usage( -exitval => 0, -verbose => 2 ) if $man;
sub main {
my $var_name = get_envdot_filepaths_var_name();
my @dotenv_filepaths;
if ( exists $ENV{$var_name} ) {
@dotenv_filepaths = interpret_dotenv_filepath_var( $ENV{$var_name} );
}
elsif ($read_from_stdin) {
croak 'Error: Option not implemented';
}
else {
if ( !-f $dotenv_filepath ) {
print {*STDERR} "Error: File not found: '$dotenv_filepath'\n"
or croak 'Cannot print error message';
return EXIT_ERROR_NO_FILE;
}
@dotenv_filepaths = ($dotenv_filepath); # The CLI parameter
}
my @vars;
foreach my $dotenv_filepath ( reverse @dotenv_filepaths ) {
local $EVAL_ERROR = undef;
my @these_vars;
eval { @these_vars = get_dotenv_vars($dotenv_filepath); 1; } or do {
my $e = $EVAL_ERROR;
my ( $err, $l, $fp ) = extract_error_msg($e);
print {*STDERR} 'Error: ' . $err . ( $l ? qq{ line $l} : q{} ) . ( $fp ? qq{ file '$fp'} : q{} ) . "\n"
or croak 'Cannot print error message';
return EXIT_ERROR_OTHER_ERROR;
};
push @vars, @these_vars;
}
$_->{'opts'}->{'export'} = $export foreach (@vars);
print {*STDOUT} convert_variables_into_commands( $shell, @vars )
or croak 'Cannot print variables to STDOUT';
return EXIT_SUCCESS;
}
exit main(@ARGV);
__END__
=pod
=encoding UTF-8
=head1 NAME
envdot - Read .env file and turn its content into environment variables for different shells.
=head1 VERSION
version 0.018
=head1 SYNOPSIS
envdot [options]
eval `envdot`
Options:
--help
--man
--version
--export --no-export
--shell -s
--dotenv -e
=head2 CLI interface without dependencies
The F<envdot> command is also available
as a self contained executable.
You can download it and run it as it is without
additional installation of CPAN packages.
Of course, you still need Perl, but Perl comes with any
normal Linux installation.
This can be convenient if you want to, for instance,
include F<envdot> in a docker container build.
curl -LSs -o envdot https://raw.githubusercontent.com/mikkoi/env-dot/main/envdot.self-contained
chmod +x ./envdot
=head1 DESCRIPTION
B<envdot> reads your F<.env> file and converts it
into environment variable commands suitable for
different shells (shell families): B<sh>, B<csh> and B<fish>.
F<.env> files can be written in different flavors.
B<envdot> supports the often used B<sh> compatible flavor and
the B<docker> flavor which are not compatible with each other.
If you have several F<.env> files, you can read them in at one go
with the help of the environment variable B<ENVDOT_FILEPATHS>.
Separate the full paths with 'B<:>' character.
Env::Dot will load the files in the B<reverse order>,
starting from the last. This is the same ordering as used in B<PATH> variable:
the first overrules the following ones, that is, when reading from the last path
to the first path, if same variable is present in more than one file, the later
one replaces the one already read.
If you have set the variable ENVDOT_FILEPATHS, then B<envdot> will use that.
Otherwise, it uses the command line parameter.
If no parameter, then default value is used. Default is the file
F<.env> in the current directory.
=head1 NAME
envdot - Read .env file and turn its content into environment variables for different shells.
=head1 OPTIONS
=over 8
=item B<--help>
Print a brief help message and exits.
=item B<--man>
Prints the manual page and exits.
=item B<--version>
Prints the version and exits.
=item B<--export>, B<--no-export>
Write commands to set variables for local shell or for exporting them.
You usually want to export the variables
to all subsequent programs and subshells, i.e.
make them into I<environment variables>.
Default: export
=item B<-s>, B<--shell>
Which shell (family) are you using? Supported: sh, csh, fish.
Default: sh
=item B<-e>, B<--dotenv>
Path to F<.env> file.
Default: current directory F<.env>
=back
=head1 EXAMPLES
eval `envdot --no-export --shell csh`
eval `envdot --dotenv subdir/.env`
ENVDOT_FILEPATHS='../.env:subdir/.env:.env' eval `envdot`
=head1 DEPENDENCIES
No external dependencies outside Perl's standard distribution.
=head1 SEE ALSO
L<Env::Assert> will verify that you certainly have those environmental
variables you need. It also has an executable which can, for example,
be used to perform the check in the beginning of a B<docker> container run.
L<Dotenv> is another package which implements functionality to use
F<.env> files in Perl.
L<shdotenv|https://github.com/ko1nksm/shdotenv> is a project to provide dotenv
for shells with support for POSIX-compliant and multiple .env file syntax.
=head1 AUTHOR
Mikko Koivunalho <mikkoi@cpan.org>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2023 by Mikko Koivunalho.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
|