Porównanie folderów

10

Mam dwa foldery o podobnych strukturach podfolderów, które chciałbym porównać. Na przykład:

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

i

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

Chciałbym wymienić wszystkie te podfoldery, z Aktórych są prefiksy dla podfolderu, Boraz wymienić odpowiednie podfoldery B. Oczekiwany wynik to

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

Drugie wymaganie: jeśli wiele pasuje B, to powinno dać błąd / ostrzeżenie.

Moje rozwiązanie :

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

Problem:

Dla tych wartości $f, które nie mają odpowiednich podfolderów B, konstrukcja tablicy daje mi błąd. na przykład:

ls: nie można uzyskać dostępu do „child-4 *”: nie ma takiego pliku ani katalogu

Pytanie

  • Jak pozbyć się tych błędów?
  • Czy istnieje lepszy sposób na osiągnięcie celów niż ten w moim kodzie?

Z góry dziękuję!

Mike VDC
źródło
4
+1 za dostarczenie prawie działającego rozwiązania!
user5325
To nie jest odpowiedź na konkretne pytanie, ale możesz użyć diff -rq DIR1 DIR2do porównania nie tylko struktury katalogów, ale zawartości pliku.
jrw32982 obsługuje Monikę

Odpowiedzi:

10

Lepszy sposób

Nie parsujls ; zamiast tego użyj globów. W rzeczywistości już używasz globów, po prostu je pakujesz ls, co jest bezcelowe. Musisz tylko nullglobwłączyć, gdy nie ma żadnych dopasowań.

Również unikanie cdupraszcza rzeczy.

#!/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

Wynik:

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/

Dodałem, B/child-3-somethingaby przetestować wymaganie wtórne. To tworzy strukturę katalogów do testowania:

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}

Nawiasem mówiąc, ShellCheck jest bardzo przydatny do znajdowania problemów w skryptach powłoki.

wjandrea
źródło
ShellCheck.net jest interesujący, czy wiesz, czy przesyła wszystko na własne serwery, czy wszystko odbywa się lokalnie? Zastanawiam się tylko o prywatności wprowadzonych informacji. [Instalacja shellcheckpakietu byłaby najbezpieczniejsza]
Xen2050
@ Xen2050 Po prostu próbowałem wyłączyć mój internet, gdy jestem na stronie, i wydaje się, że się przesyła. Wyobrażam sobie, że to nie utrzymuje, ale nie jestem pewien. I tak, pakiet jest dobry; Używam wtyczki Atom, która go używa.
wjandrea
Dziękuję za sugestie. A także mnóstwo podziękowań za wskazanie w kierunku ShellCheck. Uwielbiam tę część, w której nie tylko mówi o twoich błędach, ale także daje sugestie! @ Xen2050, o części przesyłania, po prostu zainstalowany shellcheckprzy użyciu apt, a następnie sieć niepełnosprawnej. Wygląda na to, że działa bez internetu .
Mike VDC,
2

Wywołanie lsnieistniejącego folderu powoduje wyświetlenie komunikatu o błędzie. Prosty sposób, aby po prostu zignorować to poprzez zastąpienie linii 5 w skrypcie z tym: new_dirs=(`ls -1d $f* 2> /dev/null`);.

Causeon
źródło
Testowałeś to? Stderr wydaje się być domyślnie ignorowany, gdy uruchamiam t=(`echo ok; echo err 1>&2`)$ t (lub ${t[@]}) zawiera tylko ok, err jest widoczny w terminalu, ale i tak nie jest zapisywany. Czy jest coś śmiesznego w moim teście?
Xen2050,