一時ファイルに書き込まないsed -iの代替

一時ファイルに書き込まないsed -iの代替

「タグ」を削除し、数字などに置き換えてテンプレートテキストファイルを編集するいくつかのスクリプトがあります。このために私は使用します

sed -i

注文する。ただし、スクリプトが実行されているサーバーで書き込み/読み取り時間の問題が発生し、sed -iコマンドが実行されるたびにディスクに一時ファイルを書き込むため、スクリプトの実行に時間がかかります。

交換するたびにディスクに一時ファイルを書き込まない別の方法はありますか?テキストファイルをメモリ内で編集し、すべての置換が行われた後にのみ書き込むことはできますか?それとも、同じsedコマンドに複数の代替エントリを積み重ねることができますか?

明確にするために、スクリプトの形式は次のとおりです。

input=shiftLeft.txt
while IFS= read -r line
do
    sed -i "s/install, element = $line, at=/install, element = $line, at= -0.001 +/g" processedFiles/layoutDB.seq
done < "$input"

つまり、あるテキストファイルから値を読み込み、その値に基づいて別のテキストファイルでいくつかの変更を行います。多数の値に対してこれを繰り返します。

答え1

問題は、sed -i多くの一時ファイルが生成されるのではなく、同じ入力ファイルで複数回実行され、各ファイルが次のように出力用の一時ファイルを生成することですstrace

execve("/bin/sed", ["sed", "-i", "-e", "", "/tmp/foo"], 0x7fff10da5288 /* 36 vars */) = 0
openat(AT_FDCWD, "/tmp/foo", O_RDONLY)  = 3
openat(AT_FDCWD, "/tmp/sedVdjaBk", O_RDWR|O_CREAT|O_EXCL, 0600) = 4
rename("/tmp/sedVdjaBk", "/tmp/foo")    = 0
+++ exited with 0 +++

解決策はsed -i一度だけ実行することです。

これを行うには、まずsed入力ファイルをsedプログラムに変換するコマンドを作成します。それは次のとおりです。

sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!"

(たとえば、入力ファイルに有効な正規表現文字が含まれている場合はこれを改善できますが、s/install, element = &, at=/\& -0.001 +/gこれはこの質問の範囲外です。)

これをテストして、生成されたスクリプトに満足していることを確認してください。

sedその後、変換されたテキストをプログラムファイルとして使用するには別のものが必要です。標準入力から読み取るようにプログラムに指示することでこれを行うことができます(他のオプションもあります)。

sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!' \
    shiftLeft.txt |
sed -f - -i processedFiles/layoutDB.seq

-i希望の効果が満足するまでフラグなしで再テストしてください。


¹私たちが使っているから、手続き型置換を使用できます。

sed -f <(sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!' shiftLeft.txt) \
    -i processedFiles/layoutDB.seq

標準シェルでは、変換されたテキストを文字列としてキャプチャし、コマンドラインスクリプトとして提供する必要があります。

sed -e "$(sed -e 's!.*!s/install, element = &, at=/install, element = &, at= -0.001 +/g!' shiftLeft.txt)" \
    -i processedFiles/layoutDB.seq

答え2

シェルループでsedを繰り返し呼び出す代わりに、awkを1回だけ呼び出します。たとえば、(テストするサンプル入力/出力を提供していないためテストされていません)、「内部」編集にGNU awkを使用し、match():

awk -i inplace '
    NR==FNR { lines[$0] }
    (FNR>NR) && match($0,/(.*install, element = )([^,]+)(, at=)/,a) && (a[2] in lines) {
        $0 = a[0] " -0.001 +"
    }
    { print }
' shiftLeft.txt processedFiles/layoutDB.seq

入力/出力の状況によっては、これを行うより良い方法があるかもしれません。

関連情報