特定の行番号の文字を置き換えるには、awkを使用してファイルを1行ずつ読みます。

特定の行番号の文字を置き換えるには、awkを使用してファイルを1行ずつ読みます。

ここにこのスクリプトがあります。 LineNumbers.fileファイルを1行ずつ読み取り(各行には行番号が含まれている)、それに応じて0 / 0を./に置き換えてループを実行する必要があります。 BEFORE_File.txtにあります。動作しますが、100を超えるエントリではなく、LineNumbers.fileファイルの最後の行のみが必要です。

私はここで何が間違っているのかわかりません。 LineNumbers.fileを1行ずつ読み取るのに役立ちますか?

すでに利用可能ですが、sed -i "${line}s/0\/0/\.\/\./" "${myFileTmp}"3 GBを超える大容量ファイルの場合、速度は本当に遅いです。だから私はawkがより速いオプションになると思います。

とても感謝しています!

cat ./LineNumbers_TEMP/LineNumbers.file | while read line
do
myFileTmp=BEFORE_File.txt
awk -v var=${line} 'FNR==var { sub(/0\/0/, "\.\/\."); print }' "${myFileTmp}" > AFTER_File.txt
done

たとえば、ファイルは次のようになります。

cat ./LineNumbers_TEMP/LineNumbers.file
1
2
5

スクリプトの前のFile.txt:

cat BEFORE_File.txt
0/0
0/0
0/1
0/1
0/0
0/0
0/0

スクリプトを実行した後、ファイルは次のようになります。

cat AFTER_File.txt
./.
./.
0/1
0/1
./.
0/0
0/0

現在私はこれだけを得ます:

./.

答え1

LineNumbers.fileで読み取った各行番号に対して変更するため、コードは機能しません。オリジナル BEFORE_File.txtしたがって、AFTER_File.txtfinalには、AFTER_File.txtにリストされている最後の行番号に対する変更のみが含まれますLineNumbers.file

さらに、1行を変更するためにファイル全体を解析し、複数回実行することは非常に非効率的であり、その行に対する変更が同じ場合は2倍効率的です。

まず行番号を読み、次にすべての行を一度に変更することをお勧めします。

awk 'FNR == NR { lineno[$1] = 1; next }
     (FNR in lineno) && $0 == "0/0" { $0 = "./." }
     { print }' LineNumbers.file BEFORE_File.txt >AFTER_File.txt

FNR現在のファイルの現在のレコード番号(デフォルトは行番号)とこれまでに読み込んだすべてのレコード(行)の数を保持するNR2つの特殊変数です。awk~のため最初ファイルを入力すると2つの値が同じになり、同じ場合は行番号を連想配列にキーとして保存linenoし、次の行にジャンプします。

同じでない場合は、現在の行番号が配列のキーであり、lineno現在の行が同じかどうかをテストします0/0。その場合はに変更してください./.。最後{ print }のブロックは、変更の有無にかかわらず、2番目のファイルのすべての行を出力します。


まったく異なるアプローチはsed次のとおりです。sedスクリプトの作成必要なものを変更します。

行番号が与えられると、sed式nはで置き換えてns,^0/0$,./.,行を変更します。行が正しくない場合、変更は適用されません。避けるために、コマンド区切り文字としてコンマを使用します。n0/0./.0/0s///傾いたつまようじ症候群

私たちがしなければならないのは、各行番号に対して同様の式を作成することだけですn

sed 's#.*#&s,^0/0$,./.,#' LineNumbers.file

#ここでは区切り記号として使用していますs///&コマンドの代替部分は、入力ファイルから読み取った行番号で置き換えられます。

与えられた行番号のリストに対して、以下が生成される。

1s,^0/0$,./.,
2s,^0/0$,./.,
5s,^0/0$,./.,

これをファイルに直接適用するだけです。

sed 's#.*#&s,^0/0$,./.,#' LineNumbers.file | sed -f /dev/stdin BEFORE_File.txt >AFTER_File.txt

答え2

これがあなたに効果があるかどうかを確認しましょう:

awk '{ 
  if ( NR == FNR ) { 
    n[$1] = 0 
  } else { 
    if ( FNR in n ) { 
      gsub(/^0\/0$/, "./.", $0) 
    } 
    print 
  } 
}' LineNumbers.file BEFORE_File.txt > AFTER_File.txt

出力:

./.
./.
0/1
0/1
./.
0/0
0/0

答え3

入力内容が実際にblabla 4858 ABC 0/0:4,3,2 0/1:4,3,2質問に投稿した例と似ていると仮定すると、必要なものは次のとおりです。

awk 'NR==FNR{a[$1]; next} FNR in a{sub("0/0","./.")} 1' LineNumbers.file BEFORE_File.txt >AFTER_File.txt

次の質問については、実際の入力に似たサンプル入力を投稿して、必要以上に単純すぎる、複雑な、または実際にない入力にのみ機能する答えを取得しないようにしてください。

いろいろな意味で悪いアプローチなので、これをしないでください。しかし、参考に質問のようなシェルループを使用している場合は、次のように書くことができます。

myFileTmp=$(mktemp)
cp BEFORE_File.txt AFTER_File.txt
while IFS= read -r line
do
    awk -v var="${line}" '
        FNR==var { sub("0/0", "./.") } { print }
    ' AFTER_File.txt > "$myFileTmp" &&
    mv "$myFileTmp" AFTER_File.txt
done < LineNumbers.file

また、あなたの質問のスクリプト("\.\/\."gsub())は文字列です。正規表現で文字列の正規表現メタ文字をエスケープする必要はありません。同じ上記と/。あなたが書く必要がある"./."のは単なるものです。シェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?http://porkmail.org/era/unix/award.htmlhttps://mywiki.wooledge.org/Quotes現在経験している問題に加えて、スクリプトにはいくつかの他の問題があります。

答え4

行番号を持つファイルの行は、getlineを介してawkの変数として直接読み取ることができます(行番号がソートされていると仮定)。

getline var <"filename"

スクリプト全体は、次のようにawkへの単一の呼び出しになります。

awk -v f1='./LineNumbers.file' '
       NR >var+0 {    rc=getline var <f1;
                      if(rc<0){  stderr = "cat 1>&2";
                                 print "error reading",f1 | stderr;
                                 close(stderr);
                                 exit 1
                              }
                 }
       NR==var+0 {    sub(/0\/0/,"./.")
                 }
     1' BEFORE_File.txt

もちろん、目的のファイルに出力をリダイレクトしてください。

関連情報