특정 줄 번호의 문자를 바꾸기 위해 awk를 사용하여 파일을 한 줄씩 읽습니다.

특정 줄 번호의 문자를 바꾸기 위해 awk를 사용하여 파일을 한 줄씩 읽습니다.

여기에 이 ​​스크립트가 있습니다. LineNumbers.file 파일을 한 줄씩 읽고(각 줄에는 줄 번호가 포함됨) 그에 따라 0/0을 ./로 바꾸어 루프를 실행해야 합니다. BEFORE_File.txt에 있습니다. 작동하지만 100개 이상의 항목이 아닌 LineNumbers.file 파일의 마지막 줄만 필요합니다.

내가 여기서 뭘 잘못하고 있는지 잘 모르겠습니다. LineNumbers.file을 한 줄씩 읽을 수 있도록 도와주실 수 있나요?

이미 사용할 수 있지만 sed -i "${line}s/0\/0/\.\/\./" "${myFileTmp}"3GB가 넘는 대용량 파일의 경우 속도가 정말 느립니다. 그래서 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.

또한 한 줄을 변경하기 위해 전체 파일을 구문 분석한 다음 여러 번 수행하는 것은 매우 비효율적이며 해당 줄에 대한 수정 사항이 동일할 때 두 배 더 효율적입니다.

먼저 줄 번호를 읽은 다음 모든 줄을 한 번에 수정하는 것이 좋습니다.

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

FNR현재 파일의 현재 레코드 번호(기본값은 줄 번호)와 지금까지 읽은 모든 레코드(줄) 수를 보유하는 NR두 개의 특수 변수입니다 . awk~을 위한첫 번째파일을 입력하면 두 값이 같을 것이고, 같을 때는 줄번호를 연관배열에 키로 저장 lineno하고 다음 줄로 점프한다.

동일하지 않은 경우 현재 행 번호가 배열의 키인지 lineno, 그리고 현재 행이 동일한지 테스트합니다 0/0. 그렇다면 로 변경하세요 ./.. 마지막 { print }블록은 수정 여부에 관계없이 두 번째 파일의 모든 줄을 출력합니다.


완전히 다른 접근 방식은 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.html, 그리고https://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

물론 원하는 파일로 출력을 리디렉션하세요.

관련 정보