5

sed を使用して、%XXX% 形式のファイル内のテンプレート文字列を、シェル スクリプト内の XXX という変数の値に置き換えようとしています。

例:以下は完全に機能します

sed "s/%user_home%/$user_home/gi"

したがってuser_home=fred、次の場合、

NameVirtualHost *:80

<VirtualHost *:80>
  ServerName %server_name%

  ErrorLog /var/log/apache2/%user_home%_webapp_error.log
  CustomLog /var/log/apache2/%user_home%_webapp.log common

  DocumentRoot /home/%user_home%/web_app/public
</VirtualHost>

となり、

NameVirtualHost *:80

<VirtualHost *:80>
  ServerName %server_name%

  ErrorLog /var/log/apache2/fred_webapp_error.log
  CustomLog /var/log/apache2/fred_webapp.log common

  DocumentRoot /home/fred/web_app/public
</VirtualHost>

問題は、テンプレート文字列とその変数を事前に明示的に知らずに sed コマンドを実行したいことです。つまり、%XXX% を検索し、変数の実際の名前を気にせずに、それを $XXX の内容に置き換えます。

後方参照と関係があることは知っていますが、後方参照の内容を変数名として使用する方法がわかりません。

私は試した、

sed "s/%\([a-z_]\)%/$(\1)/gi"

しかし、$\1 という変数を探しているように見えるため、これは機能しませんでした。

4

4 に答える 4

3

ここでの問題は、sedコマンドが実際に実行されるまでに (したがって、変数名を取得するまでに)、sedコマンドが完全にアセンブルされている必要があることです (Bash 変数の値を置換文字列に置き換えることを含む)。そのため、すべてが間違った順序で発生します。

または、より高いレベルのビューを取ると、問題はsedBash 変数について知らないため、変数の詳細を提供するために Bash が必要ですが、Bash はsed置換について知らないため、方法がありません。詳細が必要な変数を知ることです。

Bash 変数を使用したい限り、修正はより多くの Bash を使用することです。最初に を呼び出す前に、関連する変数名を特定する必要がありますsed。以下は、その方法を示しています。


ファイル内のすべての変数名のリストを取得するには、次のように記述できます。

grep -o '%[a-z_][a-z_]*%' FILE | grep -o '[a-z_][a-z_]*' | sort -u

(1 つ目grepは、形式のすべての式を取得します%...%。2 つ目grepは、パーセント記号を除外します。または、必要に応じてパーセント記号を使用することもできますsed。必要変数名sort -uのリストだけが異なるため、重複を排除します。)

sedそれを武器に、必要なすべての置換を実行するコマンドを組み立てることができます。

sed_args=()
while read varname ; do
    sed_args+=(-e "s/%$varname%/${!varname}/g")
done < <(grep -o '%[a-z_][a-z_]*%' FILE | grep -o '[a-z_][a-z_]*' | sort -u)
sed "${sed_args[@]}" FILE

(「の値を変数名として取得し、その変数の値を返す${!varname}」という意味の の使用に注意してください。これは、 Bash リファレンス マニュアルの §3.5.3「シェル パラメータの展開」が「間接展開」と呼んでいるものです。 )$varname

これを関数でラップできます。

function replace_bash_variables () {
    local file="$1"
    local sed_args=()
    local varname
    while read varname ; do
        sed_args+=(-e "s/%$varname%/${!varname}/g")
    done < <(grep -o '%[a-z_][a-z_]*%' "$file" | grep -o '[a-z_][a-z_]*' | sort -u)
    if [[ "${#sed_args[@]}" = 0 ]] ; then
        # if no variables to replace, just cat the file:
        cat -- "$file"
    else
        sed "${sed_args[@]}" -- "$file"
    fi
}

replace_bash_variables OLD_FILE > NEW_FILE

ファイルを 2 回読み取る必要がないように、上記を調整して行ごとの処理を行うこともできます。(ファイルを 2 回読み取るということは、実際のファイルを渡す必要があり、これをパイプラインの出力に (たとえば) 適用できないため、柔軟性が高くなります。)

于 2013-10-25T05:32:33.473 に答える
0

あなたを使っawkてこれを行うことができます

awk '{gsub(/%user_home%/,"${user_home}")}1' file
NameVirtualHost *:80

<VirtualHost *:80>
  ServerName %server_name%

  ErrorLog /var/log/apache2/${user_home}_webapp_error.log
  CustomLog /var/log/apache2/${user_home}_webapp.log common

  DocumentRoot /home/${user_home}/web_app/public
</VirtualHost>

%user_home%これは変数に置き換えます${user_home}

于 2013-10-25T05:24:31.137 に答える
0

1 sed で試しますが、変数の名前と値を知るために「設定」コンテンツを事前にキャッチする必要があります

#!/bin/ksh
# YourFilename contain the file name of your file to treat (here passed as 1st parameter to a script)
YourFileName=$1

(set | sed 's/.*/#V0r:&:r0V#/'; cat ${YourFileName}) | sed -n "
s/$/²/
H

$  {
   x
   s/^\(\n *\)*//
# also reset t flag
   t varxs

:varxs
   s/^#V0r:\([a-zA-Z0-9_]\{1,\}\)=\([^²]*\):r0V#²\(\n.*\)%\1%/#V0r:\1=\2:r0V#²\3\2/
   t varxs
: tmpb

# clean the line when no more occurance in text
#   s/^#V0r:\([a-zA-Z0-9_]\{1,\}\)=\([^²]*\):r0V#²\n//
   s/^[^²]*:r0V#²\n//

# and next
   t varxs


# clean the  marker
   s/²\(\n\)/\1/g
   s/²$//

# display the result
   p
   }
"
  • ここでの制限は、エスケープされていない文字「²」の使用によるものであるため、ファイルに ² が表示された場合、迷惑になる可能性があります (この文字をマーカーとして変更するか、ファイルで翻訳してください)。
  • #V0r: と :r0V# もマーカーであり、問​​題なく変更できます
于 2013-10-25T14:13:57.510 に答える