私のスクリプトに問題があります。
全奏曲 まず、次の100行のファイルのリストがあります。
100;TEST ONE
101;TEST TWO
...
200;TEST HUNDRED
各行には2つのパラメータがあります。たとえば、最初の行のパラメータは「645」、「TEST ONE」です。したがって、セミコロンは区切り記号です。
2つの変数に2つのパラメータを含める必要があります。 $idと$nameだとしましょう。 $ idと$ nameの値は行ごとに異なります。たとえば、2行目の場合は、$ id = "646"と$ name = "TEST TWO"です。
その後、サンプルファイルをインポートして、事前定義されたキーワードを$ idと$ nameの値に変更する必要があります。サンプルファイルは次のとおりです。
xxx is yyy
だから私は異なる内容の100ファイルが欲しいです。各ファイルには、各行の$ idおよび$ nameデータを含める必要があります。そして$ nameの値に名前を付ける必要があります。
私のスクリプトがあります。
#!/bin/bash -x
rm -f output/*
for i in $(cat list)
do
id="$(printf "$i" | awk -F ';' '{print $1}')"
name="$(printf "$i" | awk -F ';' '{print $2}')"
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/$name.xml
done
だから私はリストファイルを一行ずつ読みました。各行に2つの変数をインポートし、それを使用してサンプルファイルのキーワード(xxxとyyy)を置き換え、結果を保存します。
しかし、何かが間違っています。
その結果、出力ファイルは1つだけです。そしてデバッグがひどいようです。
これはデバッグウィンドウです。私のリストファイルには2行しかありません。出力ファイルのみを取得します。ファイル名は「TEST」で、「101 is TEST」という文字列が含まれます。
「Test One」と「Test Two」の2つのファイルが必要です。「100はTest One」と「101はTest Two」を含める必要があります。
ご覧のとおり、2番目の変数(「TEST ONE」など)にスペースがあります。問題が空白の特殊記号に関連しているようですが、理由がわかりません。 -F awkパラメーターを ";"に設定したため、awkはセミコロンのみの区切り文字として解釈する必要があります。
私は何が間違っていましたか?
答え1
私が正しく理解したら、whileループと変数拡張を使用できます。
while IFS= read -r line; do
id="${line%;*}"
name="${line#*;}"
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/"$name".xml
done < file
@steeldriverが提案したように、ここに(よりエレガントな)オプションがあります:
while IFS=';' read -r id name; do
cp sample.xml output/input.tmp
sed -i -e "s/xxx/$id/g" output/input.tmp
sed -i -e "s/yyy/$name/g" output/input.tmp
mv output/input.tmp output/"$name".xml
done < file
答え2
引用! ! . この行への参照がありません。
mv output/input.tmp output/$name.xml
それが必要です:
mv output/input.tmp output/"$name".xml
スペースを含むファイル名の問題を回避します。
また、の拡張は$(cat list)
シェルによって分割(束)され、これは空間を分割することもあります。
おそらく次のスクリプトに変更できます。
#!/bin/bash -x
rm -f output/*
inputfile=output/input.tmp
while read -r line
do
id=${line%%;*}
name=${line##*;}
cp sample.xml "$inputfile"
sed -i -e "s/xxx/$id/g" "$inputfile"
sed -i -e "s/yyy/$name/g" "$inputfile"
mv "$inputfile" output/"$name".xml; echo
done <list
答え3
awkが期待した結果を生成できないのは、ファイルを繰り返す方法です。を使用して繰り返す場合は、行でfor i in $(cat file)
はなく単語(IFSに分割)を繰り返します。ファイルを1行ずつ読み取るには、次のようにしますwhile read
。
while read -r line; do
...
done < file
詳細については、次のbash FAQを参照してください。ファイル(データストリーム、変数)を1行ずつ(および/またはフィールドごとに)読み取る方法は?
答え4
あるいは、awkを使用してこれを行うことができます。ラインごとに4つのプロセスではなく1つのプロセスに。これは、リストに行が多いがexample.xmlが小さい場合に便利です。
awk -F';' 'FNR==NR{x=x $0 RS; next}
{t=x; gsub(/xxx/,$1,t); gsub(/yyy/,$2,t); f="output/"$2".xml"; printf "%s",t >f; close(f)}
' sample.xml list
# shown with unnecessary linebreaks for clarity, but you can put it all on one line
Qで説明されているように、リストにCRLF行末(別名DOSまたはWindows形式)があり、最初に削除できない場合、または削除したくない場合は、awkは2番目の{
挿入直後でも処理できます。sub(/\r$/,"",$0);
(または$2
必要に応じて)。
Perlもこれを行うことができますが(perlはawkが行うことができるほとんどすべての操作を実行できます)、もう少し冗長でPerlが一般的に使用されますが、awkのようなPOSIXではありません。