두 개의 파일이 있는데 각각 약 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/2
out
i
약간만 수정하면 첫 번째 파일의 헤더를 기반으로 출력 파일의 이름을 지정할 수 있습니다(두 번째 파일의 헤더는 무시하고 순서가 같다고 가정합니다).
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