Сравнение папок

10

У меня есть две папки со схожими структурами подпапок, которые я хотел бы сравнить. Например:

A 
├── child-1
├── child-2
├── child-3
├── child-4
├── child-5

и

B 
├── child-1-some-text
├── child-2-more-text
├── child-3-nothing
├── child-6-random-text
├── child-7-more-random-text

Я хотел бы перечислить все те подпапки, из Aкоторых есть префикс для подпапки, Bи перечислить также соответствующие подпапки B. Ожидаемый результат

child-1 -- child-1-some-text
child-2 -- child-2-more-text
child-3 -- child-3-nothing

Второстепенное требование: если несколько совпадений B, то должно появиться сообщение об ошибке / предупреждение.

Мое решение :

cd A
for f in `ls -d */`; 
do
    cd B;
    new_dirs=(`ls -1d $f*`);
    cd -;
    if [ ${#new_dirs[@]} -eq 0 ]
    then
        ## DO_Nothing
        continue;
    elif  [ ${#new_dirs[@]} -gt 1 ]
    then
        echo "Multiple matches to $f";
        continue;
    else
        echo "Unique Match found to $f -- ${new_dirs[0]}";
        continue;
    fi;    
done

Проблема:

Для тех значений $f, в которых нет соответствующих подпапок B, конструкция массива выдает ошибку. например:

ls: не может получить доступ к 'child-4 *': нет такого файла или каталога

Вопрос

  • Как избавиться от этих ошибок?
  • Есть ли лучший способ достичь цели, чем в моем коде?

Заранее спасибо!

Майк VDC
источник
4
+1 за предоставление почти рабочего решения!
user5325
Это не ответ на ваш конкретный вопрос, но вы можете использовать его diff -rq DIR1 DIR2для сравнения не только структуры каталогов, но и содержимого файлов.
jrw32982 поддерживает Монику

Ответы:

10

Лучший способ

Не разбирайсяls ; вместо этого используйте шарики. На самом деле вы уже используете шарики, просто оборачивая их ls, что бессмысленно. Вам просто нужно nullglobвключить, когда нет совпадений.

Также избегать cdупрощает вещи.

#!/bin/bash

shopt -s nullglob

dir1=A
dir2=B

for dir in "$dir1"/*/; do
    basename="$(basename -- "$dir")"
    dirs_match=( "$dir2/$basename"*/ )
    case ${#dirs_match[@]} in
    0)
        ;;
    1)
        echo "Unique match for $dir: ${dirs_match[*]}"
        ;;
    *)
        echo "Multiple matches for $dir: ${dirs_match[*]}" >&2
        ;;
    esac
done

Вывод:

Unique match for A/child-1/: B/child-1-some-text/
Unique match for A/child-2/: B/child-2-more-text/
Multiple matches for A/child-3/: B/child-3-nothing/ B/child-3-something/

Я добавил, B/child-3-somethingчтобы проверить вторичное требование. Это создает структуру каталогов для тестирования:

mkdir -p A/child-{1..5} B/child-{1-some-text,2-more-text,3-nothing,3-something,6-random-text,7-more-random-text}

Кстати, ShellCheck очень полезен для поиска проблем в сценариях оболочки.

wjandrea
источник
ShellCheck.net интересен, вы знаете, загружает ли он все на свои серверы, или все это делается локально? Просто интересно о конфиденциальности введенной информации. [Установка shellcheckпакета будет самой безопасной]
Xen2050
@ Xen2050 Только что попробовал отключить мой интернет, пока на сайте, и похоже, что загружается. Я предположил бы, что это не держит это, но не уверен. И да, пакет хороший; Я использую плагин Atom, который использует его.
wjandrea
Спасибо за предложения. А также огромное спасибо за указание на ShellCheck. Мне понравилась часть, где она не только сообщает вам ваши ошибки, но и дает советы! @ Xen2050, про загрузку, я только что установил shellcheckс помощью aptи затем отключил сеть. Вроде работает без интернета .
Майк VDC
2

При вызове lsнесуществующей папки выдается сообщение об ошибке, с которым вы столкнулись. Самый простой способ это просто игнорировать это, заменив строку 5 в сценарии с этим: new_dirs=(`ls -1d $f* 2> /dev/null`);.

cauon
источник
Вы проверяли это? Stderr, кажется, игнорируется по умолчанию, когда я запускаю t=(`echo ok; echo err 1>&2`)$ t (или ${t[@]}), содержит только ok, ошибка отображается в терминале, но не сохраняется в любом случае. Или в моем тесте есть что-то смешное?
Xen2050