txt 파일의 특정 위치에 있는 데이터를 다른 파일의 데이터로 바꾸기

txt 파일의 특정 위치에 있는 데이터를 다른 파일의 데이터로 바꾸기

다음 형식의 텍스트 파일이 있습니다.

$data This is the experimental data    
good data
This is good file
datafile
1 4324 3673 6.2e+11 7687 67576
2 3565 8768 8760 5780 8778          "This is line '2'"
3 7656 8793 -3e+11 7099 79909
4 8768 8965 8769 9879 0970
5 5878 9879 7.970e-1 9070 0709799
.
.
.
100000 3655 6868 97879 96879 69899
$.endfile

행 "2"에서 "100000"까지의 세 번째 및 네 번째 열의 데이터를 각각 하나의 열과 99999개의 행으로 구성된 두 개의 다른 텍스트 파일의 데이터로 바꾸고 싶습니다.

awk이 작업을 수행하려면 다른 UNIX 명령을 어떻게 사용합니까 sed? 열 구분 기호는 공백입니다.

다른 두 텍스트 파일은 각각 99999줄로 구성되며 형식은 다음과 같습니다.

12414
12421
36347
3.4e+3
-3.5e22
987983
.
.
.
87698

답변1

이상한 방법 :

awk '{if(FNR==NR){f2[FNR+1]=$1;} 
      else{
        if(FNR==1){k++;} 
        if(k==1){f3[FNR+1]=$1} 
        else{if($1~/^[0-9]+/ && $1>1){$3=f2[$1];$4=f3[$1];} 
         print}
  }}' file2 file3 file1 

명확하게 하기 위해 이는 주석 스크립트로 작성된 것과 동일합니다.

#!/usr/local/bin/gawk -f

{
    ## NR is the current line number, irrespective of 
    ## which input file is being read. FNR is the line 
    ## number of the current file. It is reset to 1 each 
    ## time a new file is opened. Therefore, FNR will be 
    ## equal to NR only while the 1st file is being read.
    if(FNR==NR){
        ## If this is the 1st file, save its 1st field
        ## in the array f2. The key of the array is the
        ## line number of the current file plus one. This is
        ## because you want to start modifying from row '2' onwards.
        ## Therefore, presumably, you want the 1st row of file2 to
        ## be the value for row '2' of your data file..
        f2[FNR+1]=$1;
    } 
    ## If this is not the 1st file
    else{
        ## If this is the 1st line of the current file
        if(FNR==1){
            ## Increase the value of the variable k by 1.
            k++;
        } 
        ## If k is currently 1, this means that the above has only
        ## been run once so we are currently reading the 1nd file.
        if(k==1){
            ## Save the 1st field of this file (file3 in your example)
            ## in the array f3. The key of the array is the
            ## line number of the current file plus one. 
            f3[FNR+1]=$1
        }
        ## If k is not 1, we are reading the 3rd file. In this case, 
        ## your actual data.
        else{
            ## If the 1st field is a number and is greater than 1.
            ## In other words, if this is one of the lines you want
            ## to change. 
            if($1~/^[0-9]+/ && $1>1){
                ## Set the 3rd field to be the value saved in the array
                ## f2 for the value of $1.  
                $3=f2[$1];
                ## Set the 4th field to be the value saved in the array
                ## f3 for the value of $1. 
                $4=f3[$1];
            } 
            ## Print the current line. Since this is outside the
            ## previous if block, it will print all lines irrespective
            ## of whether they've been modified. 
            print;
        }
    }
}

펄 방식:

perl -lane 'BEGIN{
    open(A,"file2"); while(<A>){chomp; $f2{$.+1}=$_;} 
    open(B,"file3"); while(<B>){chomp; $f3{$.+1}=$_;}} 
    if($F[0]=~/^\d+$/ && $F[0]>1){$F[2]=$f2{$F[0]}; $F[3]=$f3{$F[0]}}
     print "@F"' file1

설명하다

  • -lane: l각 입력 줄 끝에서 후행 줄 바꿈을 자동으로 제거하고( 와 동일) chomp각 문에 줄 바꿈을 추가합니다. 빈 공간의 각 입력 줄은 자동으로 배열로 분할되어 Perl이 awk처럼 실행됩니다 print. " 입력 파일의 각 줄에서 제공되는 스크립트를 실행합니다.a@Fn-e
  • BEGIN{...}: 입력 파일을 읽기 전에 실행됩니다. 이 예에서는 각 추가 파일을 열고 해당 내용을 해시에 저장 %f2합니다 %f3. 이것은 awk기본적으로 위에서 사용한 것과 동일한 배열입니다.
  • if($F[0]=~/^\d+$/ && $F[0]>1){...}:다시 말하지만, 이는 awk 스크립트와 동일한 논리입니다. 이 필드는 각 파일에 해당하는 항목으로 대체됩니다.
  • print "@F": 모든 필드가 인쇄됩니다.

답변2

100% 솔루션을 요구하는 것이 아니기 때문에 awk(a) 이해하기 쉽고 (b) awk메모리 제약을 강조하지 않는 하이브리드 솔루션을 제공하겠습니다.

awk '
    $1 == 2 { secondpart = 1 }
       { if (!secondpart) {
                print > "top"
         } else {
                print $1, $2 > "left"
                print $5, $6, $7, $8, $9 > "right"
         }
       }' a
(cat top; paste -d" " left b c right) > new_a
rm top left right

또는 임시 파일 중 하나를 삭제하고 하나의 명령으로 스크립트를 단축할 수 있습니다.

(awk '
    $1 == 2 { secondpart = 1 }
       { if (!secondpart) {
                print
         } else {
                print $1, $2 > "left"
                print $5, $6, $7, $8, $9 > "right"
         }
       }' a; paste -d" " left b c right) > new_a
rm left right

이렇게 하면 출력 줄 끝에 추가 공백이 생기고  a줄에 9개 이상의 필드(열)가 있으면 파일의 데이터가 손실됩니다. 이것이 문제라면 쉽게 해결할 수 있습니다.

답변3

{ { paste -d\  /dev/fd/[345] | 
    sed 's/ \( [^ ]*\)\(.*\)/\2\1/'
} 3<<FILE1 4<<FILE2 5<<FILE3
$(<file1 sed '1,/^1/w /dev/fd/2
      /^2/,$!d;s/ [^ ]*//4;s// /3')
FILE1
$(<file2 tr -s \\n)
FILE2
$(<file3 tr -s \\n)
FILE3
} 2>&1

위의 명령 시퀀스에서는 꽤 많은 입력/출력 저글링을 수행하고 있습니다. 완료하는 것은 매우 간단합니다. file[23]실제로 동일합니다. 둘 다 99,999행의 3/4행에 대한 복사본입니다. 남은 건 file1- 그게 전부야본질적으로파일은 위의 예와 정확히 동일하지만 5행이 6행과 7행에 복사되어 일치합니다 file[23].

기본적으로 각 파일에는 자체 파일 설명자와 자체 준비가 있습니다. file[23]준비가 거의 필요하지 않습니다. tr반복되는 모든 \n줄 문자를 하나로 압축하면 빈 줄이 사라집니다.

file1조금 더 얻으십시오. 먼저, a로 시작하는 첫 번째 줄까지의 모든 줄이 1기록됩니다 stderr. 다음으로 출력에서 ​​제거되므로 으로만 출력됩니다 >&2. sed다음으로 3/4 열을 선택하고 공백으로 바꿉니다. 이는 이제 원래 위치에 두 개의 연속 공백 문자가 있음을 의미합니다.

paste모든 파일 설명자를 수집하고 모두 함께 붙이고 공백으로 구분합니다. 그런 다음 sed두 공백 문자 바로 뒤에 공백이 아닌 문자의 첫 번째 시퀀스를 그 뒤에 오는 모든 문자로 바꿔 보십시오.

stderr마지막으로 파일 설명자가 stdout추가됩니다 stdout. 결과는 다음과 같습니다.

산출

$data This is the experimental data

good data

This is good file

datafile

1 4324 3673 6.2e+11 7687 67576
2 3565 8768 12414 12414 8778
3 7656 8793 12421 12421 79909
4 8768 8965 36347 36347 0970
5 5878 9879 3.4e+3 3.4e+3 0709799
6 5878 9879 -3.5e22 -3.5e22 0709799
7 5878 9879 987983 987983 0709799
. . .
. . .
. . .
100000 3655 6868 87698 87698 69899
$.endfile 

답변4

배열이 없는 또 다른 이상한 방법은 약간 지저분하므로 나중에 정리하려고 합니다.

awk 'function get(file,L) {x=1
        while ( (getline < file) > 0) {if(NR==x)y=$0;x++}
        close(file)
        return y
        }
     ARGV[1]==FILENAME{d=$0;a=get(ARGV[2],$0);b=get(ARGV[3],$0);$0=d;$2=a;$3=b;print
     }' file file1 file2

관련 정보