Program Club

bash, Linux : 두 텍스트 파일 간의 차이 설정

proclub 2020. 11. 26. 20:11
반응형

bash, Linux : 두 텍스트 파일 간의 차이 설정


나는 두 개의 파일이 A- nodes_to_deleteB- nodes_to_keep. 각 파일에는 숫자 ID가있는 여러 줄이 있습니다.

나는에있는 숫자 ID의 목록을 갖고 싶어 nodes_to_delete하지만,하지에 nodes_to_keep예를 대체 텍스트.

PostgreSQL 데이터베이스 내에서 수행하는 것은 비합리적으로 느립니다. Linux CLI 도구를 사용하여 bash에서 깔끔한 방법이 있습니까?

업데이트 : 이것은 Pythonic 작업처럼 보이지만 파일은 정말 정말 큽니다. 내가 사용하는 몇 가지 유사한 문제를 해결 한 uniq, sort어떤 집합 이론 기술을. 이것은 데이터베이스에 상응하는 것보다 약 2-3 배 더 빠릅니다.


통신의 명령은 해당 작업을 수행합니다.


누군가가 몇 달 전 sh에서 정확히이 작업을 수행하는 방법을 보여 주었는데 잠시 동안 그것을 찾을 수 없었습니다 ... 그리고 나는 당신의 질문을 우연히 발견했습니다. 여기있어 :

set_union () {
   sort $1 $2 | uniq
}

set_difference () {
   sort $1 $2 $2 | uniq -u
}

set_symmetric_difference() {
   sort $1 $2 | uniq -u
}

사용 comm-두 개의 정렬 된 파일을 한 줄씩 비교합니다.

질문에 대한 짧은 답변

이 명령은 deleteNodes에 고유 한 행을 리턴하지만 keepNodes의 행은 리턴하지 않습니다.

comm -1 -3 <(sort keepNodes) <(sort deleteNodes)

예시 설정

keepNodes라는 파일을 만들고 명령에 deleteNodes대한 정렬되지 않은 입력으로 사용 하겠습니다 comm.

$ cat > keepNodes <(echo bob; echo amber;)
$ cat > deleteNodes <(echo bob; echo ann;)

기본적으로 인수없이 comm을 실행하면 다음 레이아웃으로 3 개의 열이 인쇄됩니다.

lines_unique_to_FILE1
    lines_unique_to_FILE2
        lines_which_appear_in_both

위의 예제 파일을 사용하여 인수없이 comm을 실행합니다. 세 개의 열에 유의하십시오.

$ comm <(sort keepNodes) <(sort deleteNodes)
amber
    ann
        bob

열 출력 억제

-N으로 열 1, 2 또는 3을 억제합니다. 열이 숨겨지면 공백이 줄어 듭니다.

$ comm -1 <(sort keepNodes) <(sort deleteNodes)
ann
    bob
$ comm -2 <(sort keepNodes) <(sort deleteNodes)
amber
    bob
$ comm -3 <(sort keepNodes) <(sort deleteNodes)
amber
    ann
$ comm -1 -3 <(sort keepNodes) <(sort deleteNodes)
ann
$ comm -2 -3 <(sort keepNodes) <(sort deleteNodes)
amber
$ comm -1 -2 <(sort keepNodes) <(sort deleteNodes)
bob

정렬이 중요합니다!

먼저 파일을 정렬하지 않고 comm을 실행하면 정렬되지 않은 파일에 대한 메시지와 함께 정상적으로 실패합니다.

comm: file 1 is not in sorted order


comm 이러한 종류의 사용 사례를 위해 특별히 설계되었지만 정렬 된 입력이 필요합니다.

awk세트 차이를 찾는 것이 매우 간단하고, 필요하지 않으며 sort, 추가적인 유연성을 제공 하기 때문에 틀림없이 더 나은 도구입니다 .

awk 'NR == FNR { a[$0]; next } !($0 in a)' nodes_to_keep nodes_to_delete

예를 들어 음수가 아닌 숫자를 나타내는 행의 차이 만 찾고 싶을 수 있습니다.

awk -v r='^[0-9]+$' 'NR == FNR && $0 ~ r {
    a[$0]
    next
} $0 ~ r && !($0 in a)' nodes_to_keep nodes_to_delete

아마도 postgres에서 더 나은 방법이 필요할 수도 있습니다. 플랫 파일을 사용하여 더 빠른 방법을 찾지 못할 것입니다. 간단한 내부 조인을 수행하고 두 ID 열이 모두 매우 빠르게 인덱싱되었다고 가정 할 수 있어야합니다.


So, this is slightly different from the other answers. I can't say that a C++ compiler is exactly a "Linux CLI tool", but running g++ -O3 -march=native -o set_diff main.cpp (with the below code in main.cpp can do the trick):

#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>

using namespace std;

int main(int argc, char** argv) {
    ifstream keep_file(argv[1]), del_file(argv[2]);
    unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
    string line;
    while (getline(del_file, line)) {
        init_lines.erase(line);
    }
    copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}

To use, simply run set_diff B A (not A B, since B is nodes_to_keep) and the resulting difference will be printed to stdout.

Note that I've forgone a few C++ best practices to keep the code simpler.

Many additional speed optimizations could be made (at the price of more memory). mmap would also be particularly useful for large data sets, but that'd make the code much more involved.

데이터 세트가 크다고 말씀 하셨기 때문에 nodes_to_delete한 번에 한 줄씩 읽는 것이 메모리 소비를 줄이는 데 좋은 생각이라고 생각했습니다. 위 코드에서 취한 접근 방식은 nodes_to_delete. 또한 질서는 보존되지 않습니다.


복사 및 붙여 넣기가 더 쉬운 것 bash(예 main.cpp: 생성 건너 뛰기 ) :

g++ -O3 -march=native -xc++ -o set_diff - <<EOF
#include<algorithm>
#include<iostream>
#include<iterator>
#include<fstream>
#include<string>
#include<unordered_set>

using namespace std;

int main(int argc, char** argv) {
        ifstream keep_file(argv[1]), del_file(argv[2]);
        unordered_multiset<string> init_lines{istream_iterator<string>(keep_file), istream_iterator<string>()};
        string line;
        while (getline(del_file, line)) {
                init_lines.erase(line);
        }
        copy(init_lines.begin(),init_lines.end(), ostream_iterator<string>(cout, "\n"));
}
EOF

참고 URL : https://stackoverflow.com/questions/2509533/bash-linux-set-difference-between-two-text-files

반응형