bash는 다른 파일에서 새 열 추가 / 추가
한 열의 name.txt 파일이 있습니다. 예 :
A
B
C
D
E
F
그런 다음 egxtxt, y.txt 및 z.txt 파일이 많이 있습니다.
x.txt에는
A 1
C 3
D 2
y.txt는
A 1
B 4
E 3
z.txt에는
B 2
D 2
F 1
바람직한 출력은 다음과 같습니다 (매핑이없는 경우 0으로 채움).
A 1 1 0
B 0 4 2
C 3 0 0
D 2 0 2
E 0 3 0
F 0 0 1
bash로 만들 수 있습니까? (아마도 awk?)
많은 감사 !!!
첫 번째 편집-나의 잠정적 노력
나는 bash를 처음 접 했기 때문에 awk로 가능한 해결책을 찾기가 정말 어렵습니다. 저는 R에 더 익숙합니다.
namematrix[namematrix[,1]==xmatrix[,1],]
모두 모두, 난 정말 대해 자세히 알아 내게 도움 아래 종류의 도움을 주셔서 감사 awk
하고 join
!
두 번째 편집-매우 효율적인 접근 방식이 밝혀졌습니다!
운 좋게도 아래의 몇 가지 훌륭한 답변에서 영감을 받아 계산적으로 매우 효율적인 방법을 아래와 같이 분류했습니다. 이는 특히 매우 큰 크기의 매우 많은 수의 파일을 처리하는 경우 유사한 질문에 직면하는 다른 사람들에게 도움이 될 수 있습니다.
먼저 join_awk.bash를 터치합니다.
#!/bin/bash
join -oauto -e0 -a1 $1 $2 | awk '{print $2}'
예를 들어, name.txt 및 x.txt에 대해이 bash 스크립트를 실행하십시오.
join_awk.bash name.txt x.txt
생성 할 것이다
1
0
3
2
0
0
여기에서는 디스크 공간을 절약하기 위해 두 번째 열만 유지합니다. 데이터 세트에서 첫 번째 열은 엄청난 디스크 공간을 차지하는 매우 긴 이름이기 때문입니다.
그런 다음 간단히 구현하십시오.
parallel join_awk.bash name.txt {} \> outdir/output.{} ::: {a,b,c}.txt
이것은 GNU parallel 및 join을 사용하는 아래의 훌륭한 답변에서 영감을 얻었습니다. 차이점은 직렬 추가 논리로 인해 아래 답변이 지정해야한다는 것 j1
입니다. parallel
이로 인해 실제로 "병렬"이 아닙니다. 또한 직렬 추가가 계속됨에 따라 속도가 느려지고 느려집니다. 반대로 여기서는 각 파일을 병렬로 개별적으로 조작합니다. 다중 CPU로 많은 수의 대용량 파일을 처리 할 때 매우 빠를 수 있습니다.
마지막으로 모든 단일 열 출력 파일을 다음과 같이 병합하십시오.
cd outdir
paste output* > merged.txt
이것은 paste
본질적으로 병렬 이기 때문에 매우 빠릅니다 .
답변
이것을 사용할 수 있습니다 awk
:
awk 'NF == 2 {
map[FILENAME,$1] = $2
next
}
{
printf "%s", $1 for (f=1; f<ARGC-1; ++f) printf "%s", OFS map[ARGV[f],$1]+0
print ""
}' {x,y,z}.txt name.txt
A 1 1 0
B 0 4 2
C 3 0 0
D 2 0 2
E 0 3 0
F 0 0 1
한 가지 더 방법을 추가합니다. 표시된 샘플로 다음과 같이 작성하고 테스트 해 보시기 바랍니다. IMHO는 모든 awk
에서 작동해야 하지만 GNU 3.1 버전 awk
만 있습니다. 이것은 매우 간단하고 일반적인 방법입니다. first (major) Input_file의 읽기에서 배열을 만든 다음 나중에 각 파일 0
에서 해당 배열의 요소가 특정 Input_file에서 발견되지 않는 사람을 추가 하고 주어진 작은 샘플로만 테스트합니다.
awk '
function checkArray(array){
for(i in array){
if(!(i in found)){ array[i]=array[i] OFS "0" }
}
}
FNR==NR{
arr[$0] next } foundCheck && FNR==1{ checkArray(arr) delete found foundCheck="" } { if($1 in arr){
arr[$1]=(arr[$1] OFS $2) found[$1]
foundCheck=1
next
}
}
END{
checkArray(arr)
for(key in arr){
print key,arr[key]
}
}
' name.txt x.txt y.txt z.txt
설명 : 위에 대한 자세한 설명을 추가합니다.
awk ' ##Starting awk program from here.
function checkArray(array){ ##Creating a function named checkArray from here.
for(i in array){ ##CTraversing through array here.
if(!(i in found)){ array[i]=array[i] OFS "0" } ##Checking condition if key is NOT in found then append a 0 in that specific value.
}
}
FNR==NR{ ##Checking condition if FNR==NR which will be TRUE when names.txt is being read.
arr[$0] ##Creating array with name arr with index of current line. next ##next will skip all further statements from here. } foundCheck && FNR==1{ ##Checking condition if foundCheck is SET and this is first line of Input_file. checkArray(arr) ##Calling function checkArray by passing arr array name in it. delete found ##Deleting found array to get rid of previous values. foundCheck="" ##Nullifying foundCheck here. } { if($1 in arr){ ##Checking condition if 1st field is present in arr.
arr[$1]=(arr[$1] OFS $2) ##Appening 2nd field value to arr with index of $1.
found[$1] ##Adding 1st field to found as an index here.
foundCheck=1 ##Setting foundCheck here.
next ##next will skip all further statements from here.
}
}
END{ ##Starting END block of this program from here.
checkArray(arr) ##Calling function checkArray by passing arr array name in it.
for(key in arr){ ##Traversing thorugh arr here.
print key,arr[key] ##Printing index and its value here.
}
}
' name.txt x.txt y.txt z.txt ##Mentioning Input_file names here.
예, 할 수 있습니다 awk
. 예, 도구입니다. 배열과 일반 파일의 줄 번호 (사용 FNR
기록의 파일 번호 ) 및 총 라인 ( NR
기록을 당신이에서 모든 문자를 읽을 수 있습니다) names.txt
로 a[]
배열 한 다음 변수에 파일 번호를 추적 fno
, 당신은 모든 추가 사항에서 추가 할 수 있습니다 x.txt
다음 첫 번째 다음 파일의 라인 (처리하기 전에 y.txt
마지막 파일에서 볼 모든 문자 이상), 루프를, 그리고 사람들을 위해 하지 보이는 장소 a를 0
, 다음 정상적으로 처리를 계속합니다. 각 추가 파일에 대해 반복하십시오.
추가 라인 별 설명은 주석에 표시됩니다.
awk '
FNR==NR { # first file
a[$1] = "" # fill array with letters as index fno = 1 # set file number counter next # get next record (line) } FNR == 1 { fno++ } # first line in file, increment file count fno > 2 && FNR == 1 { # file no. 3+ (not run on x.txt) for (i in a) # loop over letters if (!(i in seen)) # if not in seen array a[i] = a[i]" "0 # append 0 delete seen # delete seen array } $1 in a { # if line begins with letter in array
a[$1] = a[$1]" "$2 # append second field seen[$1]++ # add letter to seen array
}
END {
for (i in a) # place zeros for last column
if (!(i in seen))
a[i] = a[i]" "0
for (i in a) # print results
print i a[i]
}' name.txt x.txt y.txt z.txt
사용 / 출력 예시
위의 내용을 복사하고 파일이 들어있는 현재 디렉토리가있는 xterm에 마우스 가운데 붙여 넣기 만하면 다음을 받게됩니다.
A 1 1 0
B 0 4 2
C 3 0 0
D 2 0 2
E 0 3 0
F 0 0 1
자체 포함 된 스크립트 만들기
명령 줄에 붙여 넣는 대신 실행할 스크립트를 만들려면 내용을 작은 따옴표로 묶지 않고 포함시킨 다음 파일을 실행 가능하게 만드십시오. 예를 들어 인터프리터를 첫 번째 줄로 포함하고 내용을 다음과 같이 포함합니다.
#!/usr/bin/awk -f
FNR==NR { # first file
a[$1] = "" # fill array with letters as index fno = 1 # set file number counter next # get next record (line) } FNR == 1 { fno++ } # first line in file, increment file count fno > 2 && FNR == 1 { # file no. 3+ (not run on x.txt) for (i in a) # loop over letters if (!(i in seen)) # if not in seen array a[i] = a[i]" "0 # append 0 delete seen # delete seen array } $1 in a { # if line begins with letter in array
a[$1] = a[$1]" "$2 # append second field seen[$1]++ # add letter to seen array
}
END {
for (i in a) # place zeros for last column
if (!(i in seen))
a[i] = a[i]" "0
for (i in a) # print results
print i a[i]
}
awk
주어진 순서대로 인자로 주어진 파일 이름을 처리합니다.
사용 / 출력 예시
스크립트 파일을 사용하여 (파일을 넣은 names.awk
다음 chmod +x names.awk
실행 가능하게 만드는 데 사용 ) 다음을 수행합니다.
$ ./names.awk name.txt x.txt y.txt z.txt
A 1 1 0
B 0 4 2
C 3 0 0
D 2 0 2
E 0 3 0
F 0 0 1
추가 질문이 있으면 알려주세요.
다른 접근 방식 GNU awk
$ cat script.awk NF == 1 { name[$1] = $1 for (i = 1; i < ARGC - 1; i++) { name[$1] = name[$1] " 0" } next } { name[$1] = gensub(/ ./, " " $2, ARGIND - 1, name[$1])
}
END {
for (k in name) {
print name[k]
}
}
스크립트 호출 :
$ awk -f script.awk name.txt {x,y,z}.txt
A 1 1 0
B 0 4 2
C 3 0 0
D 2 0 2
E 0 3 0
F 0 0 1
출력은와 동일한 순서를 보여 name.txt
주지만 모든 종류의 입력에 대해 사실이라고 생각하지 않습니다.
이것은 당신을 위해 일할 수 있습니다 (GNU 병렬 및 조인).
cp name.txt out && t=$(mktemp) && parallel -j1 join -oauto -e0 -a1 out {} \> $t \&\& mv $t out ::: {x,y,z}.txt
출력은 파일에 있습니다 out
.
당신이 사용할 수있는 join
join -a1 -e0 -o '0,2.2' name.txt x.txt | join -a1 -e0 -o '0,1.2,2.2' - y.txt | join -a1 -e0 -o '0,1.2,1.3,2.2' - z.txt
와 bash
방법에 대한 :
#!/bin/bash
declare -A hash # use an associative array
for f in "x.txt" "y.txt" "z.txt"; do # loop over these files
while read -r key val; do # read key and val pairs
hash[$f,$key]=$val # assign the hash to val done < "$f"
done
while read -r key; do
echo -n "$key" # print the 1st column for f in "x.txt" "y.txt" "z.txt"; do # loop over the filenames echo -n " ${hash[$f,$key]:-0}" # print the associated value or "0" if undefined
done
echo # put a newline
done < "name.txt"