如何递归获取包含与 Bash 中包含目录同名的 txt 文件的所有目录?

如何递归获取包含与 Bash 中包含目录同名的 txt 文件的所有目录?

假设我有以下内容:

./
   Dir1/
      Sub1/
           Sub1.jpg
      Sub2/
           Sub2.jpg
           Sub2.png
           Sub2.txt
   Dir2/
      Sub1/
           Sub1.doc
           Sub1.txt
      Sub2/
           Sub2.jpg
           Sub2.png
           SomeTxt.txt
   Dir3/
      Dir3.txt
   Dir4/
      Sub1/
           Dir4.txt
      Some.txt

使用 bash(在我的情况下是 bashrc),如何获取包含所有包含与包含目录同名的文本文件(.txt)的目录的变量?因此,从上面的示例中,我希望有一个包含以下内容的变量:

./Dir1/Sub2/    
./Dir1/Sub1/
./Dir3/

它不应该返回,./Dir1/Sub1/因为它没有Sub1.txt文件。

它不应该返回,./Dir2/Sub2/因为其中的 txt 文件不是Sub2.txt

它不应该返回./Dir4/./Dir4/Sub1/因为Dir4.txt不是直接在./Dir4/

笔记

目录名称不必特别为这些名称。重要的是 txt 文件的名称与包含目录的名称相匹配。

另外,我知道其他语言可能更容易,但我需要在 bash 中使用它。

答案1

鉴于

$ tree somepath/
somepath/
├── Dir1
│   ├── Sub1
│   │   └── Sub1.jpg
│   └── Sub2
│       ├── Sub2.jpg
│       ├── Sub2.png
│       └── Sub2.txt
├── Dir2
│   ├── Sub1
│   │   ├── Sub1.doc
│   │   └── Sub1.txt
│   └── Sub2
│       ├── SomeTxt.txt
│       ├── Sub2.jpg
│       └── Sub2.png
├── Dir3
│   └── Dir3.txt
└── Dir4
    ├── Some.txt
    └── Sub1
        └── Dir4.txt

9 directories, 12 files

然后,启用 shell 的 globstar 选项(shopt -s globstar

for d in somepath/**/; do
    d="${d%/}"
    [[ -f "${d}/${d##*/}.txt" ]] && printf "%s\n" "$d"
done
somepath/Dir1/Sub2
somepath/Dir2/Sub1
somepath/Dir3

如果要将结果存储在数组中以供以后处理,则可以使用 shell 的内置函数mapfile(或其同义词readarray)来实现,例如

mapfile -t TXT_DIRS < <(
  for d in somepath/**/; do
    d="${d%/}"
    [[ -f "${d}/${d##*/}.txt" ]] && printf "%s\n" "$d"
  done
)

笔记: 更好的做法是使用在 bash:在 select 中使用 find 进行空格安全程序化

然后你可以像这样迭代数组

for d in "${TXT_DIRS[@]}"; do
    echo "$d"
done
somepath/Dir1/Sub2
somepath/Dir2/Sub1
somepath/Dir3

或者,您可以完全避免存储结果,只需在找到每个目录时对其进行处理,即

for d in somepath/**/; do 
    d="${d%/}"
    [[ -f "${d}/${d##*/}.txt" ]] && echo "Doing something with $d"
done
Doing something with somepath/Dir1/Sub2
Doing something with somepath/Dir2/Sub1
Doing something with somepath/Dir3

答案2

这实际上与steeldriver的答案相同,但它循环遍历每个txt文件而不是每个目录。希望它也更容易理解。

由于您在评论中提到您实际上并不需要数组,因此我省略了该步骤。

shopt -s globstar
for f in ./**/*.txt; do
    d="$(dirname "$f")"
    if [[ $(basename "$f") == $(basename "$d").txt ]]; then
        printf '%s\n' "$d"
    fi
done

输出:

./Dir1/Sub2
./Dir2/Sub1
./Dir3

相关内容