두 개의 큰 파일에서 n번째 열을 반복적으로 읽고, 나란히 잘라내고 붙여넣어 새 n번째 파일을 만듭니다.

두 개의 큰 파일에서 n번째 열을 반복적으로 읽고, 나란히 잘라내고 붙여넣어 새 n번째 파일을 만듭니다.

두 개의 파일이 있는데 각각 약 100,000개의 공백으로 구분된 열이 있습니다. 두 파일의 각 열을 추출하고 별도의 파일에 쓴 다음 명령 2를 실행하고 싶습니다.

두 개의 열이 있는 파일의 예입니다.

cat test1.txt
rr1 rr2
1 2
1 2
1 1
2 1

cat test2.txt
rr1 rr2
2 2
1 1
2 1
2 2

test1.txt의 첫 번째 열과 test2.txt의 첫 번째 열을 가져와서 이 두 열이 나란히 있는 새 파일이라는 새 파일을 만들고 싶습니다. 지금까지 시도한 코드는 다음과 같습니다.

awk -F' ' '{
  for(i=1; i<=NF; i++){ # iterate over each column
    paste -d' ' <(sed 1d test1.txt | awk -v var1="$i" '{print $var1}') <(sed 1d test2.txt | awk -v var2="$i" '{print $var2}' ) > out$i
    # write to file named with column name or i
    # do command2 for out$1
  } 
}'  

그러나 이 코드는 오류를 발생시킵니다.

awk: cmd. line:3:     paste -d
awk: cmd. line:3:             ^ unexpected newline or end of string

예상 출력

cat out1
1 2
1 1
1 2
2 2

cat out2
2 2
2 1
1 1
1 2

나는 이것에 많은 시간을 보냈지만 이 문제를 해결하는 방법을 모릅니다. 어떤 도움이라도 대단히 감사하겠습니다. 더 좋은 방법이 있나요?

답변1

이렇게 사용하세요그리고도구 상자에서 열 수와 일반적인 명령을 가져옵니다.

#!/bin/bash

for i in  $(seq 1 $(awk '{print NF;exit}' test1.txt)); do
    paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
          <(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done

또는

#!/bin/bash

numcols=$(awk '{print NF;exit}' test1.txt)
for ((i=1; i<=numcols; i++)); do 
    paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
          <(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done

또는 사용:

#!/bin/ksh

numcols=$(awk '{print NF;exit}' test1.txt)
for i in  {1..$numcols}; do
    paste <(sed 1d test1.txt | cut -d ' ' -f"$i") \
          <(sed 1d test2.txt | cut -d ' ' -f"$i") > "out.$i"
done

그 다음에:

cat out.1
cat out.2

댓글에 설명되어 있듯이, 당신 awk처럼 혼합할 수는 없습니다 shell.

개발자가 아니라면 여기에서 했던 것처럼 기본 쉘 명령을 익히는 것이 가장 좋습니다.

다음 기본 명령에 대한 설명서를 읽어보세요.

  • tr
  • paste
  • sed
  • seq

그리 기본적이지는 않습니다(여기서는 간단한 방법으로 사용됨).

  • awk

임시 파일 이름으로 교체 >(command ...)또는 교체를 처리합니다. <(...)이 파일을 쓰거나 읽으면 바이트가 내부 명령으로 파이프됩니다. 일반적으로 파일 리디렉션과 함께 사용됩니다 cmd1 2> >(cmd2).

바라보다:
http://mywiki.wooledge.org/ProcessSubstitution
http://mywiki.wooledge.org/BashFAQ/024

답변2

가정:

  • 모든 파일에는 최소한 한 줄(제목)이 있습니다.
  • 모든 파일의 줄 수는 동일합니다.
  • 모든 파일의 열 개수는 동일합니다.
  • awk모든 파일은 (배열을 통해 ) 메모리에 저장될 수 있습니다 .

일반적인 접근:

  • 다차원 배열을 사용할 GNU awk수 있지만 부작용은 (단일 차원 인덱스보다) 더 많은 메모리를 사용한다는 것입니다.
  • column # (NF)row number (FNR)인덱스 ++를 사용하여 1차원 배열 에 데이터를 저장합니다.file count
  • END{...}블록 에서는 데이터를 out{1..NF}파일 로 인쇄하는 배열을 반복합니다.

다음만 사용하세요 awk:

$ cat merge.awk

FNR==1 { fcnt++ }                                       # keep track of number of files
FNR>1  { for (i=1; i<=NF; i++)                          # loop through columns
             lines[i,FNR,fcnt]=$i                       # index = column # + row number + file count
       }
END    { for (i=1; i<=NF; i++) {                        # loop through columns
             for (j=2; j<=FNR; j++)                     # loop through rows
                 for (k=1; k<=fcnt; k++)                # loop through filecount
                     printf "%s%s", lines[i,j,k], (k<fcnt ? OFS : ORS), lines[i,j,k] > ("out" i)
             close ("out" i)
         }
       }

OP의 두 파일에 대해 실행하십시오.

$ awk -f merge.awk test1.txt test2.txt

$ head out?
==> out1 <==
1 2
1 1
1 2
2 2

==> out2 <==
2 2
2 1
1 1
1 2

세 개의 새 파일:

$ head t?.txt
==> t1.txt <==
rr1 rr2 rr3
1 2 3
4 5 6
7 8 9

==> t2.txt <==
rr1 rr2 rr3
a b c
d e f
g h i

==> t3.txt <==
rr1 rr2 rr3
X XX XXX
Y YY YYY
Z ZZ ZZZ

다음 세 파일에 대해 실행합니다.

$ awk -f merge.awk t1.txt t2.txt t3.txt

$ head out?
==> out1 <==
1 a X
4 d Y
7 g Z

==> out2 <==
2 b XX
5 e YY
8 h ZZ

==> out3 <==
3 c XXX
6 f YYY
9 i ZZZ

답변3

이 오류는 작은따옴표로 묶인 문자열 안에 작은따옴표를 사용할 수 없다는 사실에서 발생합니다. 이 awk명령은 프로그램을 paste -d프로그램 awk(잘림으로 인한 구문 오류 포함)으로 처리하고 나머지 코드(따옴표가 없는 다음 공백까지)를 처리할 첫 번째 파일 이름 등으로 처리합니다. 또한 쉘 프로그램 내에서는 명령을 사용할 수 없습니다 awk.

awk다음 파이프라인은 사용된 명령 에 두 개의 파일을 나란히 공급합니다 paste. 이 awk명령은 각 파일의 열 쌍을 각 열의 다른 출력 파일로 출력합니다.

$ paste test1.txt test2.txt | awk 'NR > 1 { for (i = 1; i <= NF/2; ++i) print $i, $(NF/2+i) >("out" i) }'
$ cat out1
1 2
1 1
1 2
2 2
$ cat out2
2 2
2 1
1 1
1 2

awk아름답게 인쇄된 코드 :

NR > 1 {
    for (i = 1; i <= NF/2; ++i)
        print $i, $(NF/2+i) > ("out" i)
}

NF/2첫 번째 줄에 입력된 헤더를 무시하고 이 코드는 파일 중 하나의 필드를 반복합니다(두 파일 모두 동일한 수의 필드를 갖고 두 파일의 필드가 동일한 순서로 쌍을 이루어야 한다고 가정합니다). 들판, 즉 우리에게 들판의 절반이 주어집니다. 그런 다음 필드 번호 뒤에 이름이 붙은 파일 에 해당 번호를 추가하여 i해당 번호와 함께 번째 필드를 인쇄합니다 .NF/2outi

약간만 수정하면 첫 번째 파일의 헤더를 기반으로 출력 파일의 이름을 지정할 수 있습니다(두 번째 파일의 헤더는 무시하고 순서가 같다고 가정합니다).

NR == 1 {
    for (i = 1; i <= NF/2; ++i) head[i] = $i
    next
}

{
    for (i = 1; i <= NF/2; ++i)
        print $i, $(NF/2+i) > head[i]
}

질문에 제공된 데이터를 기반으로 두 파일이 모두 생성되고 rr1( rr2또는 이미 존재하는 경우 덮어씁니다).


아래 주석에서 올바르게 지적했듯이(현재 주석은 삭제됨) 위의 내용은 100000개 열에 대해 "열린 파일이 너무 많습니다" 오류를 일으킬 수 있으며 awk해당 구현은 열린 파일 설명자 풀을 지능적으로 관리하지 않습니다(GNU처럼 awk). . 다른 awk구현에서는 매번 출력 파일 을 닫고 print.>>>

awk위의 마지막 클립을 수정한 버전은 다음과 같습니다.

NR == 1 {
    for (i = 1; i <= NF/2; ++i) head[i] = $i
    next
}

{
    for (i = 1; i <= NF/2; ++i) {
        print $i, $(NF/2+i) >> head[i]
        close(head[i])
    }
}

답변4

col_co=$(awk 'END{print NF}' f1.txt)
for ((i=1;i<=$col_co;i++))
do 
awk -v i="$i" 'NR>1{print $i}' f1.txt|paste >file_1.txt
awk -v i="$i" 'NR>1{print $i}' f2.txt >file_2.txt
paste file_1.txt file_2.txt >out_new_$i.txt
done

산출

cat out_new_1.txt
1       2
1       1
1       2
2       2


cat out_new_2.txt
2       2
2       1
1       1
1       2

관련 정보