get variables substituted when I cat a file

吃可爱长大的小学妹 提交于 2019-12-06 02:33:22

This would be trivial to attempt in Python, and see if that works for you. You could use the re.sub function to replace all occurrences of some pattern like "\$\w+" by calling a function which does the transformation (rather than a specific string to replace it with). And for the replacement function you could use os.getenv(), which of course takes a variable name and returns its value.

Edit: Here's a complete Python script that does the above:

#!/usr/bin/python

import fileinput
import os
import re

def transform(match):
    return os.getenv(match.group(1)) # replace the "capture" to omit $

for line in fileinput.input(): # reads from stdin or from a file in argv
    print re.sub('\$(\w+)', transform, line), # comma to omit newline

The obvious way to do this has a lot of problems:

# This will fail with certain inputs.  HTML will certainly be a problem
# as the '<' and '>' characters will be interpreted as file redirects
$ while read r; do eval echo $r; done < input

The following perl should handle the problem fairly well for simple inputs.

$ perl -pwe 'while(($k,$v) = each %ENV ) { s/\${?$k}?/$v/ }' input

But it doesn't do anything with constructs like ${FOO-bar}. If you need to handle such constructs, it might be sufficient to escape all of the shell meta-characters and do the while/read loop:

$ sed -e 's/\([<>&|();]\)/\\\1/g' input | while read -r l; do eval echo "$l"; done

Note that this is neither robust nor secure. Consider what happens on input like:

\; rm -rf /

I said "consider". Do not test that. The sed will insert a backslash before the semicolon, the eval will then get the string "\\;" which will be interpreted as a single backslash followed by a semi-colon which termintates the echo, and the rm -rf will be executed. Given the insecurity of evaling unknown input, it would probably be safer to stick with something like perl and explicitly replace the desired sh constructs. Something like:

$ perl -pwe 'while(($k,$v) = each %ENV ) { s/\${?$k}?/$v/ }; 
    s/\${[^-]*-([^}]*)}/$1/g' input

This one has problems with input like ${FOO=some-text}. In order to reliably get all of the sh constructs (${word:rhs} where the ':' can be any of '-', '?', '=', '+', '%', '#' or any of the same with a colon prepended (or a lot of other symbols if you allow non-posix sh syntax!)) you would have to construct a fairly elaborate set of comparisons.

cat copies its inputs unchanged to its outputs - at least in its original (1st Edition UNIX) form. It didn't have any options to start with. Then BSD added a bunch, and the original UNIX team objected: 'cat came back from Berkeley waving flags' (see: 1 - passim). It should not be used to edit files - that is not its purpose. (I found a reference to the article in the BSD (Mac OS X) man page for cat: Rob Pike, "UNIX Style, or cat -v Considered Harmful", USENIX Summer Conference Proceedings, 1983. See also http://quotes.cat-v.org/programming/)

So, you need something other than cat to do the job. I'd recommend Perl or Python; either can do it pretty easily. Alternatively, consider sed, or perhaps awk.

#!/usr/bin/env perl
use strict;
use warnings;
while (<>)
{
    foreach my $key (keys %ENV)
    {
        s/\$$key\b/$ENV{$key}/g;  # $envvar
        s/\${$key}/$ENV{$key}/g;  # ${envvar}
    }
    print;
}

This loops through the input line, looking for each environment variable in turn. The alternative mechanism is to look for possible variables and do the relevant substitution. This turns out to be a little tricky, but doable:

#!/usr/bin/env perl
use strict;
use warnings;
while (<>)
{
    while (m/\$((\w+))/ || m/\$({(\w+)})/)
    {
        my $key = $2;
        my $var = $1;
        s/\$$var/$ENV{$key}/g if defined $ENV{$key};
    }
    print;
}

When I included the literal $ in the captures, the substitute operation did not work correctly.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!