複数のファイルの文字列を1つのファイルの各文字列行に置き換えます。

複数のファイルの文字列を1つのファイルの各文字列行に置き換えます。

長年このサイトを使ってきていつも回答があるので質問を一度もしませんでした(通常多くの回答)。私はこれも同じだと確信していますが、生涯にわたってそれを見つけることができません。

任意の長さの行を含むファイルを含むディレクトリがあります。

a.txt
b.txt
c.txt
d.txt

eg.txtその後、文字列のリストを含むファイルがあります。

opq  111
rst  222
uvw  333
xyz  444

各txtファイルには、置き換えたい文字列があります。

a.txt has a#P#b
b.txt has c#P#d
c.txt has e#P#f
d.txt has g#P#h

#P#文字列ファイルの2番目の「列」に置き換えたいと思います。これはファイルごとに一度だけ発生します#P#(私はそこにファイルを入れたからです)。結果は次のとおりです。

a.txt has a111b
b.txt has c222d
c.txt has e333f
d.txt has g444h

「不変」の仮定は、私のディレクトリにある行数だけeg.txtファイルがあり、.txtアルファベット順にソートされているということです。の行は、eg.txt「列」1に基づいてアルファベット順にソートされます。

forループでawkand sed(実際には)を使用してこれを実行しようとしましたが、sd「ソース」と「ターゲット」を1行ずつ読み取ることはできません。

私は結果を取得する方法は難しいことではありません。現在は多くの行やファイル(今は15行と15個のファイル)を扱っていませんが、時にはそのような場合があります。 ArchとDebianベースのLinuxディストリビューション(時にはWSL 2)でzshをシェルとして使用します。

回答がありましたらお詫び申し上げます。私はこのプロジェクトを見つけるために過去2日間で本当に一生懸命働いてきましたが、今は頭が疲れています。

編集:ディレクトリ内のファイルにさまざまな長さの行がたくさんあり、指定された文字列が#P#ファイルごとに1回だけ表示されることを明確にするために更新されました。

答え1

GNU awkを使用した「内部」編集ARGIND

awk -i inplace '
    NR == FNR { map[NR]=$2 }
    NR != FNR { sub(/#P#/,map[ARGIND]) }
1' eg.txt ?.txt

上記は、代替テキストにeg.txtスペースや&sが含まれていないと仮定しています。

答え2

準備

各ファイルには1行しかありません。

$ grep -- . ?.txt
a.txt:a#P#b
b.txt:c#P#d
c.txt:e#P#f
d.txt:g#P#h
$ cat input
opq  111
rst  222
uvw  333
xyz  444

解決策

sed各ファイルに対してシェルループ呼び出しを実行します。

for file in ?.txt; do
    read -r dummy new_string rest
    sed -- "s/#P#/$new_string/g" "$file"
done <input

a111b
c222d
e333f
g444h

ファイルの変更結果に満足したら、sed -iGNU sed、Compatible、sed -i ''FreeBSD、Compatibleに変更してください。sed

上記は、行に、または文字がinput含まれていないと仮定します。可能であれば、まずバックスラッシュのある項目をエスケープする必要があります。&/\

答え3

#!/bin/sh
mv eg.txt eg.input
awk 'NR==FNR{a[++i]=$2;next}{sub("#P#",a[++j]);print>(FILENAME".new")}' eg.input ./*.txt &&
for f in *.txt; do mv "$f.new" "$f"; done
mv eg.input eg.txt

eg.txtawk行を変更する必要があるファイルにのみ拡張するように名前を変更してから、元にeg.input戻します。*.txt

NR==FNR{    #For the first file, eg.input
  a[++i]=$2   #Put the second field in the array `a`
  next        #Skip the rest of the code
}
{                        #For the other files
  sub("#P#",a[++j])        #Make the substitution
  print>(FILENAME".new")   #Print to the line to `FILENAME`.new
}

その後、forループは古い*.txtファイルの内容をファイルの内容で上書きします*.new*.newファイルが正しいことを確認するまで、forループを抑制できます。


一部のawk実装では、多くの開かれたファイルを処理できません(GNU awkは可能です)。 awkが「開いたファイルが多すぎます」というエラーで終了した場合は、このバリエーションを使用してください。

awk 'NR==FNR{a[++i]=$2;next}FNR==1{close(fn);fn=FILENAME".new"}{sub("#P#",a[++j]);print>fn}'

答え4

eg.txt

opq  111
rst  222
uvw  333
xyz  444

a.txt

a#P#b
12345
apple

b.txt

c#P#d
56788

注文する

j=1;for i in "a.txt" "b.txt" ; do  b=`sed -n ''$j'p' eg.txt| awk '{print $2}'`;sed "s/#P#/$b/g" $i;echo "=================";j=$(($j+1)); done


output

below are the output of a.txt
a111b
12345
apple
=================
below are the output of b.txt
c222d
56788
=================

関連情報