get variables substituted when I cat a file

倾然丶 夕夏残阳落幕 提交于 2020-01-02 07:35:25

问题


Is it possible, in a clean way to get the variable values when I cat a file, instead of the variable names, as written in the file. It's hard to explain, but here goes a simple example:

$ cat <<EOF
$HOME
EOF
/home/myself

cat returns /home/myself because it is already expanded by the shell.

$ echo \$HOME >/tmp/home
$ cat /tmp/home
$HOME

cat simply reads the file, I want $HOME to be expanded here somehow by cat, because the file will contain variable names (not like HOME=/home/myself)

My question is if this is possible somehow, otherwise I will have to write some dirty code.

EDIT: they are big xml files containing

<checkbox active="$value">

true or false


回答1:


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



回答2:


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.




回答3:


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.



来源:https://stackoverflow.com/questions/6025342/get-variables-substituted-when-i-cat-a-file

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