使用 find 组合唯一的文件对

使用 find 组合唯一的文件对

我需要对相当数量的文件对文件执行一系列操作。为了简单起见,我将重点放在对一对猫进行简单的操作上,以讨论配对以及如何编写 shell 脚本中最直接的配对部分。

假设我有 4 个文件,A.txt、B.txt、C.txt 和 D.txt,我想编写一个紧凑的脚本,基本上可以执行以下操作:

 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 并不是“唯一”的。

但我想让它比 shell 脚本更容易一些,我可以对不同的文件集执行此操作,只需在目录中运行它,并让它递归地查找所有匹配项。我立刻就好像走错了方向,把事情搞砸了:

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在保存之前进行操作。这里,使用了空分隔(-d ''for mapfile(== readarray), -print0forfind-zfor sort),这需要 GNU 实用程序。

并为它们做一个双循环,从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

然后可以通过管道传输到 shell 来执行或使用/eGNU sed 中的标志来完成。

相关内容