سؤال

هل من الممكن، بطريقة نظيفة للحصول على القيم المتغيرة عندما أقطت ملف، بدلا من الأسماء المتغيرة، كما هو مكتوب في الملف.من الصعب أن تشرح، ولكن هنا يذهب مثالا بسيطا: giveacodicetagpre.

يعود القط / الصفحة الرئيسية / نفسي لأنه تم توسيعه بالفعل بواسطة Shell. giveacodicetagpre.

Cat يقوم ببساطة بقراءة الملف، أريد توسيع نطاق $ إلى توسيعه هنا بطريقة أو بأخرى، لأن الملف سيحتوي على أسماء متغيرة (وليس مثل الصفحة الرئيسية= / الصفحة الرئيسية / نفسي)

سؤالي هو إذا كان هذا ممكن بطريقة أو بأخرى، وإلا فسوف أضطر إلى كتابة بعض الكود القذر.

تحرير: فهي ملفات XML كبيرة تحتوي على giveacodicetagpre.

صحيح أو خطأ

هل كانت مفيدة؟

المحلول

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top