長年このサイトを使ってきていつも回答があるので質問を一度もしませんでした(通常多くの回答)。私はこれも同じだと確信していますが、生涯にわたってそれを見つけることができません。
任意の長さの行を含むファイルを含むディレクトリがあります。
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ループでawk
and 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 -i
GNU 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.txt
awk行を変更する必要があるファイルにのみ拡張するように名前を変更してから、元に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
=================