Как я рекурсивно получаю все каталоги, которые содержат txt файл с тем же именем как содержание каталога в Bash?

Скажем, у меня есть следующее:

./
   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

Используя удар (bashrc в моем случае), как я могу получить переменную, которая содержит все каталоги, содержащие текстовый файл (.txt) с тем же именем как содержание каталога? Таким образом от примера выше я хотел бы иметь переменную, содержащую следующее:

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

Это не должно возвращаться ./Dir1/Sub1/ потому что это не имеет a Sub1.txt файл.

Это возврат shoudn't ./Dir2/Sub2/ потому что txt файл в нем не Sub2.txt.

Это не должно возвращаться ./Dir4/ или ./Dir4/Sub1/ потому что Dir4.txt не находится непосредственно в ./Dir4/.

Примечание:

Названия каталогов не должны быть этими именами в частности. То, что важно, - то, что имя txt файла соответствует содержанию названия каталога.

Кроме того, я знаю, что другие языки могли бы быть легче, но мне нужен он в ударе.

2
задан 17 October 2017 в 06:20

2 ответа

Учитывая

$ 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

тогда, с включенной опцией оболочки 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

Если вы хотите сохранить результаты в массиве для дальнейшего использования обработки, вы можете сделать это, используя встроенную в оболочку mapfile (или ее синоним readarray), например,

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

ПРИМЕЧАНИЕ: было бы лучше завершить список нулем, используя один из методов, описанных в bash: безопасное для пробелов процедурное использование find into select

Затем вы можете перебирать массив таким образом

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

В качестве альтернативы, вы можете вообще не сохранять результаты и просто обрабатывать каждый каталог так, как вы его находите, т.е. 1115]

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
3
ответ дан 2 December 2019 в 02:45

Это - практически то же как ответ steeldriver, но это циклично выполняется по каждому txt файлу вместо по каждому dir, Надо надеяться, легче понять также.

, Так как Вы упомянули в комментарии, что Вам на самом деле не нужен массив, я не учел тот шаг.

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
0
ответ дан 2 December 2019 в 02:45

Другие вопросы по тегам:

Похожие вопросы: