make getpeername() return the original socket address which before it was intercepted
[hband-tools.git] / user-tools / substenv
blob60290be4b4befb3c0e4dbf8087f47fbbd3dcd9f4
1 #!/usr/bin/env perl
3 =pod
5 =head1 NAME
7 substenv - Substitute environment variables in parameters and run the resulting command
9 =head1 SYNOPSIS
11 substenv [I<OPTIONS>] [--] I<COMMAND> [I<ARGS>]
13 =head1 DESCRIPTION
15 Replace all occurrances of C<$NAME> in I<COMMAND> and I<ARGS> to the I<NAME> environment
16 variable's value, whatever I<NAME> would be, then run I<COMMAND> I<ARGS>.
17 Support C<${NAME}> curly bracket notation too.
19 =head1 OPTIONS
21 =over 4
23 =item -a, --all
25 Replace all occurrances of any C<$NAME> (and C<${NAME}>) substring
26 (for details see S<LIMITATIONS>).
27 This is the default behaviur, unless B<-e> is given.
29 =item -e, --environment I<NAME>
31 Replace the occurrances of I<NAME> environment variable.
32 May be specified more than once.
33 If B<-a> option is NOT given, ONLY these I<NAME>s are replaced.
35 =item -k, --keep-undefined
37 Do not replace variables which are not defined (ie. not in the environment),
38 but keep them as-is.
39 By default they are replaced with the empty string.
41 =item --dryrun, --dry-run
43 Do not run I<COMMAND>, just print what would be executed.
45 =back
47 =head1 EXAMPLE
49 This function call, in C, runs substenv(1),
50 note, there is no dollar-interpolation in C.
52 execve("substenv", ["substenv", "ls", "$HOME/.config"])
54 Then substenv issues this system call:
56 execve("ls", ["ls", "/home/jdoe/.config"])
58 =head1 LIMITATIONS
60 In "substitute all" mode (without B<-e> flag) it replaces only names
61 with uppercase letters, digits, and underscore (C<[A-Z0-9_]+>),
62 as env vars usually contain only these chars.
63 However it still replaces variables with lowercase letters in C<${NAME}> notation,
64 and specific variable(s) given in B<-e> option(s).
66 Does not honour escaped dollar marks, ie. C<\$>.
68 =head1 NOTES
70 Does not support full shell-like variable interpolation.
71 Use a real shell for it.
73 =head1 RATIONALE
75 Sometimes you don't want a shell to be in the picture when composing commands,
76 yet need to weave some environment variable into it.
78 =head1 SEE ALSO
80 envsubst(1) from gettext-base package
82 =cut
85 use Data::Dumper;
86 use Getopt::Long qw/:config no_ignore_case no_bundling no_getopt_compat no_auto_abbrev require_order/;
87 use Pod::Usage;
88 no if ($] >= 5.018), 'warnings' => 'experimental::smartmatch';
91 $OptAlwaysAll = 0;
92 $OptKeepUndef = 0;
93 $OptDryRun = 0;
94 @subst_env_only = ();
96 GetOptions(
97 'a|all!' => \$OptAlwaysAll,
98 'e|env|environ|environment=s@' => \@subst_env_only,
99 'k|keep-undefined!' => \$OptKeepUndef,
100 'dry-run|dryrun!' => \$OptDryRun,
101 'help' => sub { pod2usage(-exitval=>0, -verbose=>99); },
102 '<>' => sub { unshift @ARGV, @_[0]; die '!FINISH'; },
103 ) or pod2usage(-exitval=>2, -verbose=>99);
106 $bare_env_name_regex = '[A-Z_][A-Z0-9_]*';
107 $bracketed_env_name_regex = '[^}]+';
108 if(@subst_env_only)
110 my $specific_env_names_regex = join '|', map { quotemeta } @subst_env_only;
111 if($OptAlwaysAll)
113 $bare_env_name_regex = $specific_env_names_regex . '|' . $bare_env_name_regex;
114 $bracketed_env_name_regex = $specific_env_names_regex . '|' . $bracketed_env_name_regex;
116 else
118 $bare_env_name_regex = $bracketed_env_name_regex = $specific_env_names_regex;
122 @run_cmd = ();
124 sub subst_env
126 my $varname = shift;
127 my $whole_substring = shift;
128 if(not $OptKeepUndef or exists $ENV{$varname}) {
129 return $ENV{$varname};
131 else {
132 return $whole_substring;
136 for my $arg (@ARGV)
138 $arg =~ s/\$(\{(?<NAME>$bracketed_env_name_regex)\}|(?<NAME>$bare_env_name_regex))/subst_env($+{'NAME'}, $&)/eg;
139 push @run_cmd, $arg;
143 if($OptDryRun)
145 warn Dumper \@run_cmd;
147 else
149 exec {$run_cmd[0]} @run_cmd;
150 ($errno, $errstr) = (int $!, $!);
151 warn "$0: ${run_cmd[0]}: $errstr\n";
152 exit 125+$errno;