スクリプトに問題があります。
Prelude最初に、次のようなリスト、100行のファイルがあります。
100;TEST ONE
101;TEST TWO
...
200;TEST HUNDRED
各行には2つの引数があります。たとえば、最初の行の引数は「645」、「TEST ONE」です。したがって、セミコロンが区切り文字です。
両方の引数を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
そのため、リストファイルを1行ずつ読み取ろうとします。すべての行で2つの変数を取得し、それらを使用してサンプルファイルのキーワード(xxxおよびyyy)を置き換え、結果を保存します。
しかし、何かがうまくいかなかった
その結果、出力ファイルは1つしかありません。そして、デバッグは悪いようです。
これは、リストファイルに2行しかないデバッグウィンドウです。出力ファイルが1つしかありません。ファイル名は「TEST」だけで、「101 is TEST」という文字列が含まれています。
「TEST ONE」、「TEST TWO」の2つのファイルが必要です。「100 is TEST ONE」と「101 is TEST TWO」が含まれている必要があります。
ご覧のとおり、2番目の変数にはスペースが含まれています(「TEST ONE」など)。この問題はスペースの特殊記号に関連していると思いますが、その理由はわかりません。 -F awkパラメータを ";"に設定したので、awkはセミコロンのみを区切り文字として解釈する必要があります。
何を間違えたのですか?
私があなたを正しく理解していれば、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
引用!!この行の引用はありません:
_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
_
Awkが期待どおりの結果を生成しない理由は、ファイルを繰り返し処理する方法が原因です。 for i in $(cat file)
を使用して反復する場合、行ではなく単語(IFSによって分割)に対して反復します。ファイルを1行ずつ読み取るには、while read
を使用します。
while read -r line; do
...
done < file
詳細については、次のbash FAQを参照してください: ファイル(データストリーム、変数)を行ごと(またはフィールドごと)に読み取るにはどうすればよいですか?
別のアプローチとして、この作業はawkで実行できますを各行に4つではなく1つのプロセスで実行します。これは、リストに多数の行があるが、sample.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ではありません。