ファイルからロードされた文字列の変数をbashに強制的に展開させる


88

(ファイルからロードされた)文字列内の変数をbash(force?)で展開する方法を見つけようとしています。

「something.txt」という内容のファイルがあります。

hello $FOO world

次に実行します

export FOO=42
echo $(cat something.txt)

これは次を返します:

   hello $FOO world

変数が設定されていても、$ FOOは展開されませんでした。ファイルを評価またはソースすることはできません-ファイルを実行しようとするためです(そのままでは実行できません-変数を補間した文字列が必要です)。

何か案は?



以下の答えに対するコメントという意味ですか?
Michael Neale 2012年

私はあなたのためのコメントを意味しました。複数の回答がの使用を提案してevalおり、その影響を認識することが重要です。
追って通知があるまで一時停止します。

@DennisWilliamsonの落とし穴-返信が少し遅れていますが、良いヒントです。そしてもちろん、その間に「シェルショック」のようなものがあったので、あなたのコメントは時の試練に耐えてきました! ヒント帽子
Michael Neale 2017年

これはあなたの質問に答えますか?変数内のBash展開変数
LoganMzz

回答:


111

私は、この質問に対する答えであると思うもの、つまりenvsubstコマンドに出くわしました 。

envsubst < something.txt

ディストリビューションでまだ利用できない場合は、

GNUパッケージgettext

@Rockallite-「\ $」の問題を処理するための小さなラッパースクリプトを作成しました。

(ところで、 入力内の一部の変数のみを展開するためのenvsubstの「機能」がhttps://unix.stackexchange.com/a/294400/7088で説明されてい ますが、例外をエスケープする方がはるかに多いことに同意します便利。)

これが私のスクリプトです:

#! /bin/bash
      ## -*-Shell-Script-*-
CmdName=${0##*/}
Usage="usage: $CmdName runs envsubst, but allows '\$' to  keep variables from
    being expanded.
  With option   -sl   '\$' keeps the back-slash.
  Default is to replace  '\$' with '$'
"

if [[ $1 = -h ]]  ;then echo -e >&2  "$Usage" ; exit 1 ;fi
if [[ $1 = -sl ]] ;then  sl='\'  ; shift ;fi

sed 's/\\\$/\${EnVsUbDolR}/g' |  EnVsUbDolR=$sl\$  envsubst  "$@"

5
+1。これが唯一の答えである保証はコマンド置換、クォート除去、引数の処理などをしないに
ジョシュ・ケリー

6
ただし、完全にエクスポートされたシェル変数に対してのみ機能します(bash内)。例:S = '$ a $ b'; a =セット; export b = exported; エコー$ S | envsubst; 利回り:「エクスポート」
gaoithe 2015

1
ありがとう-私はこれらすべての年の後に、私はこの答えを受け入れるべきだと思います。
Michael Neale 2017年

1
ただし、ドル記号($)のエスケープは処理されません。たとえばecho '\$USER' | envsubst、現在のユーザー名の前にバックスラッシュを付けて出力します$USER
Rockallite 2017

3
自作のニーズがあるMacでこれを取得しているようですbrew link --force gettext
KeepCalmAndCarryOn 2017

25

答えの多くは、作業の種類を使用evalしていますechoが、複数行、シェルメタ文字のエスケープの試み、bashによる展開を意図していないテンプレート内でのエスケープなど、さまざまなことに失敗しています。

私も同じ問題を抱えていて、このシェル関数を作成しました。これは、私が知る限り、すべてを正しく処理します。これは、bashのコマンド置換ルールのために、テンプレートから末尾の改行のみを削除しますが、他のすべてがそのままである限り、これが問題になることはありません。

apply_shell_expansion() {
    declare file="$1"
    declare data=$(< "$file")
    declare delimiter="__apply_shell_expansion_delimiter__"
    declare command="cat <<$delimiter"$'\n'"$data"$'\n'"$delimiter"
    eval "$command"
}

たとえば、parameters.cfg変数を設定するだけのシェルスクリプトであるaと、template.txtそれらの変数を使用するテンプレートであるaを使用すると、次のように使用できます。

. parameters.cfg
printf "%s\n" "$(apply_shell_expansion template.txt)" > result.txt

実際には、これを一種の軽量テンプレートシステムとして使用しています。


4
これは私がこれまでに見つけた最高のトリックの1つです:)。あなたの答えに基づいて、他の変数への参照を含む文字列で変数を展開する関数を作成するのはとても簡単です-関数で$ dataを$ 1に置き換えるだけで、ここに行きます!ところで、これが元の質問に対する最良の答えだと思います。
銀河

これにも通常のeval落とし穴があります。; $(eval date)入力ファイル内の任意の行の最後に追加してみてくださいdate。コマンドが実行されます。
anubhava 2016年

1
@anubhavaどういう意味かわかりません。これは、この場合、実際に望ましい拡張動作です。言い換えれば、はい、これで任意のシェルコードを実行できるはずです。
wjl 2016

22

あなたが試すことができます

echo $(eval echo $(cat something.txt))

9
echo -e "$(eval "echo -e \"`<something.txt`\"")"新しい行を保持する必要がある場合に使用します。
pevik 2013

チャームのように機能
kyb 2018

1
しかし、あなたtemplate.txtがテキストである場合に限ります。この操作は、コマンドを実行(評価)するため危険です。
kyb 2018

2
しかし、これは二重引用符を削除しました
Thomas Decaux 2018年

サブシェルのせいにします。
ingyhere

8

各行を出力するのではなく、Bashが変数の置換を実行できるように評価する必要があります。

FOO=42
while read; do
    eval echo "$REPLY"
done < something.txt

詳細については、help evalまたはBashのマニュアルを参照してください。


2
eval危険です; $varname(の場合と同様にenvsubst)拡張されるだけでなく、拡張されます$(rm -rf ~)
チャールズ・ダフィー

4

別のアプローチ(これは厄介なようですが、とにかくここに置いています):

何か.txtの内容を一時ファイルに書き込み、echoステートメントをラップします。

something=$(cat something.txt)

echo "echo \"" > temp.out
echo "$something" >> temp.out
echo "\"" >> temp.out

次に、それを変数に戻します。

RESULT=$(source temp.out)

$ RESULTではすべてが拡張されます。しかし、それはとても間違っているようです!


1
厄介ですが、それは最も簡単で、シンプルで、機能します。
kyb 2018

3

変数参照のみを拡張したい場合(私が自分で持っていた目的)、以下を実行できます。

contents="$(cat something.txt)"
echo $(eval echo \"$contents\")

(ここでは、$ contentsを囲むエスケープされた引用符が重要です)


私はこれを試しましたが、$()は私の場合、結果の文字列を壊す行末を削除します
moz 2018年

評価行をに変更するeval "echo \"$$contents\""と、空白が保持されました
moz 2018年

1

次の解決策:

  • 定義されている変数の置換を可能にします

  • 定義されていない変数プレースホルダーは変更されません。これは、自動展開中に特に役立ちます。

  • 次の形式の変数の置換をサポートします。

    ${var_NAME}

    $var_NAME

  • 環境で定義されていない変数を報告し、そのような場合のエラーコードを返します



    TARGET_FILE=someFile.txt;
    ERR_CNT=0;

    for VARNAME in $(grep -P -o -e '\$[\{]?(\w+)*[\}]?' ${TARGET_FILE} | sort -u); do     
      VAR_VALUE=${!VARNAME};
      VARNAME2=$(echo $VARNAME| sed -e 's|^\${||g' -e 's|}$||g' -e 's|^\$||g' );
      VAR_VALUE2=${!VARNAME2};

      if [ "xxx" = "xxx$VAR_VALUE2" ]; then
         echo "$VARNAME is undefined ";
         ERR_CNT=$((ERR_CNT+1));
      else
         echo "replacing $VARNAME with $VAR_VALUE2" ;
         sed -i "s|$VARNAME|$VAR_VALUE2|g" ${TARGET_FILE}; 
      fi      
    done

    if [ ${ERR_CNT} -gt 0 ]; then
        echo "Found $ERR_CNT undefined environment variables";
        exit 1 
    fi

1
  1. 場合something.txtは、唯一持っている1本のライン、bash方法、(の短いバージョンマイケル・ニールの「不快」の回答)、使用してプロセスコマンド置換を

    FOO=42 . <(echo -e echo $(<something.txt))
    

    出力:

    hello 42 world
    

    それexportは必要ないことに注意してください。

  2. 場合something.txtが一本の以上のラインを持っている、GNUの valuateの方法:sed e

    FOO=42 sed 's/"/\\\"/g;s/.*/echo "&"/e' something.txt
    

1
私はsed自分の目的に最も合う評価を見つけました。別の構成ファイルにエクスポートされた変数から値が入力されるテンプレートからスクリプトを作成する必要がありました。コマンド展開のための適切なエスケープを処理します。たとえば、出力\$(date)を取得するため$(date)にテンプレートに配置します。ありがとう!
gadamiak

0
$ eval echo $(cat something.txt)
hello 42 world
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.

0
foo=45
file=something.txt       # in a file is written: Hello $foo world!
eval echo $(cat $file)

1
このコードスニペットをありがとうございます。これは、限られた即時のヘルプを提供する可能性があります。適切な説明が大幅にこれは問題に良い解決策であり、他、同様の質問を将来の読者にそれがより便利になるだろう、なぜ示すことによって、その長期的な価値を向上させるであろう。あなたが行った仮定を含むいくつかの説明を追加するためにあなたの答えを編集してください。
山羊座

-1

envsubst 置き換えるコンテンツが「合理的な」長さである場合は、優れたソリューションです(LenWの回答を参照)。

私の場合、変数名を置き換えるためにファイルの内容を置き換える必要がありました。envsubstコンテンツを環境変数としてエクスポートする必要があり、メガバイト程度を超える環境変数をエクスポートする場合、bashに問題があります。

awkソリューション

別の質問からのcuonglmのソリューションの使用:

needle="doc1_base64" # The "variable name" in the file. (A $ is not needed.)
needle_file="doc1_base64.txt" # Will be substituted for the needle 
haystack=$requestfile1 # File containing the needle
out=$requestfile2
awk "BEGIN{getline l < \"${needle_file}\"}/${needle}/{gsub(\"${needle}\",l)}1" $haystack > $out

このソリューションは、大きなファイルでも機能します。


-3

次の作品: bash -c "echo \"$(cat something.txt)"\"


これは非効率的であるだけでなく、非常に危険です。のような安全な拡張を実行するだけでなく$foo、のような安全でない拡張を実行します$(rm -rf ~)
チャールズ・ダフィー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.