bash는 다른 파일에서 새 열 추가 / 추가

Nov 24 2020

한 열의 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본질적으로 병렬 이기 때문에 매우 빠릅니다 .

답변

12 anubhava Nov 24 2020 at 13:42

이것을 사용할 수 있습니다 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
9 RavinderSingh13 Nov 24 2020 at 14:15

한 가지 더 방법을 추가합니다. 표시된 샘플로 다음과 같이 작성하고 테스트 해 보시기 바랍니다. 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.
6 DavidC.Rankin Nov 24 2020 at 13:35

예, 할 수 있습니다 awk. 예, 도구입니다. 배열과 일반 파일의 줄 번호 (사용 FNR 기록의 파일 번호 ) 및 총 라인 ( NR 기록을 당신이에서 모든 문자를 읽을 수 있습니다) names.txta[]배열 한 다음 변수에 파일 번호를 추적 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

추가 질문이 있으면 알려주세요.

4 Sundeep Nov 24 2020 at 14:40

다른 접근 방식 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주지만 모든 종류의 입력에 대해 사실이라고 생각하지 않습니다.

3 potong Nov 24 2020 at 19:47

이것은 당신을 위해 일할 수 있습니다 (GNU 병렬 및 조인).

cp name.txt out && t=$(mktemp) && parallel -j1 join -oauto -e0 -a1 out {} \> $t \&\& mv $t out ::: {x,y,z}.txt

출력은 파일에 있습니다 out.

2 DiegoTorresMilano Nov 24 2020 at 15:12

당신이 사용할 수있는 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
1 tshiono Nov 24 2020 at 13:48

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"