찾기를 사용하여 고유한 파일 쌍 결합

찾기를 사용하여 고유한 파일 쌍 결합

상당히 많은 수의 파일에 대해 일련의 작업을 수행해야 합니다. 단순함을 위해 한 쌍의 고양이에 대한 간단한 작업에 초점을 맞춰 페어링과 쉘 스크립트의 가장 간단한 페어링 부분을 작성하는 방법을 논의하겠습니다.

A.txt, B.txt, C.txt 및 D.txt라는 4개의 파일이 있고 기본적으로 다음을 수행하는 압축 스크립트를 작성하려고 한다고 가정해 보겠습니다.

 cat A.txt B.txt > AB.txt
 cat A.txt C.txt > AC.txt
 cat A.txt D.txt > AD.txt
 cat B.txt C.txt > BC.txt
 cat B.txt D.txt > BD.txt
 cat C.txt D.txt > CD.txt

각 고유 조합에 대한 출력을 원하며 이 기준에 따르면 AD.txt 및 DA.txt는 "고유"하지 않습니다.

하지만 저는 쉘 스크립트보다 좀 더 쉽게 만들고 싶습니다. 여기서는 다른 파일 세트에 대해 이 작업을 수행하고 디렉토리에서 실행하여 모든 일치 항목을 재귀적으로 찾도록 할 수 있습니다. 내가 길을 잘못 들어서 일을 엉망으로 만든 것처럼 보였습니다.

find "$PWD" -type f -iname "*.txt" -exec [[SOME MAGIC CODE CREATING PAIRS OF FILE NAMES]] {} \; 
 \ cat "$MAGICPAIRfile1".txt "$MAGICPAIRfile2".txt >  
 \ "$MAGICPAIRfile1"-"$MAGICPAIRfile2".txt 

이러한 부분 몇 개를 실행하려고 생각 중이었습니다. 하나는 파일 이름을 텍스트 버퍼에 덤프한 다음(파일 이름 문자열에 대한 버퍼 유형이 잘못되어 버퍼가 없음) 해당 버퍼를 다른 exec {} \; 에 전달하는 것이었습니다.

하지만 다른 사람이 좋은 생각을 가지고 있을 수도 있다고 생각했나요?

답변1

이것이 나의 제안입니다.

#!/bin/bash
files="empty"
for i in A B C D ; do
    for j in B C D ; do
     fn="$i$j"
     nf="$( echo $fn | rev )"
     # if nn is 1 $nf wasn't found in $files
      nn=1
      for q in $files ; do
        if [[ "$q" == "$nf" ]] ; then
               nn=0
         fi
        done
        if  [[  $nn -eq 1 ]] && [[ "$fn" != "$nf" ]] 
        then
           echo "cat $i.txt $j.txt >$fn.txt"
        fi
        files="$fn $nf $files"
    done
done

답변2

명령의 파일 매개변수를 find배열에 저장할 수 있습니다. 저장하기 전에도 이 작업을 수행 할 수 있습니다 sort. 여기서는 GNU 유틸리티가 필요한 null 구분 기호( -d ''for mapfile(== readarray), -print0for find-zfor )가 사용됩니다.sort

i그리고 전체 길이에서 끝까지 이중 고리를 만들고 조합을 만듭니다. 여기에서 파일 매개변수의 모든 조합을 처리할 수 있습니다.ji+1

#!/bin/bash
mapfile -d '' arr < <(find . -type f -name '*.txt' -print0 | sort -z)

for ((i=0; i<"${#arr[@]}"; i++)); do
    for ((j=i+1; j<"${#arr[@]}"; j++)); do
        printf "Processing files: %s %s\n" "${arr[i]}" "${arr[j]}"
    done
done
Processing files: ./A.txt ./B.txt
Processing files: ./A.txt ./C.txt
Processing files: ./A.txt ./D.txt
Processing files: ./B.txt ./C.txt
Processing files: ./B.txt ./D.txt
Processing files: ./C.txt ./D.txt

특정 예에서 cat파일과 원하는 출력 파일 이름(둘 다 동일한 디렉토리 수준에 있다고 가정)의 경우 을 사용하여 find ... -printf '%f\0'파일 이름만 인쇄하고 매개변수 확장을 사용하여 하위 문자열을 제거하고 명령을 생성할 수 있습니다. 개행 문자를 파일 이름 구분 기호로 사용하는 약간 수정된 버전:

#!/bin/bash
mapfile -t arr < <(find . -type f -name '*.txt' -printf "%f\n" | sort)

for ((i=0; i<"${#arr[@]}"; i++)); do
    for ((j=i+1; j<"${#arr[@]}"; j++)); do
        cat "${arr[i]}" "${arr[j]}" > "${arr[i]%.*}${arr[j]}"
    done
done

답변3

perl파일 이름이 "잘 작동"한다고 가정하고 사용할 수 있는 경우 :

find ... |
perl -0777 -MMath::Combinatorics -anE \
  'BEGIN{$,=" "}; say sort(@$_) for (combine(2, @F))' |
sort

입력시 출력 A\nB\nC\nD\n:

A B
A C
A D
B C
B D
C D

예제(GNU sed)를 다시 생성하려면:

... |
sed -E 's/([^.]+).([^ ]+) ([^.]+).([^ ]+)/cat \1.\2 \3.\4 > \1\3.\2/'
cat A.txt B.txt > AB.txt
cat A.txt C.txt > AC.txt
cat A.txt D.txt > AD.txt
cat B.txt C.txt > BC.txt
cat B.txt D.txt > BD.txt
cat C.txt D.txt > CD.txt

그런 다음 쉘에 파이프를 연결하거나 /eGNU sed의 플래그를 사용하여 실행할 수 있습니다.

관련 정보