Ordnervergleich

10

Ich habe zwei Ordner mit ähnlichen Unterordnerstrukturen, die ich vergleichen möchte. Beispielsweise:

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

und

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

Ich möchte alle Unterordner auflisten, aus Adenen ein Unterordner vorangestellt ist, Bund auch die entsprechenden Unterordner auflisten B. Die erwartete Ausgabe ist

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

Eine sekundäre Anforderung: Wenn mehrere Übereinstimmungen vorliegen B, sollte ein Fehler / eine Warnung ausgegeben werden.

Meine Lösung :

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:

Für die Werte von $f, die keine entsprechenden Unterordner enthalten B, gibt mir die Array-Konstruktion einen Fehler. z.B:

ls: kann nicht auf 'child-4 *' zugreifen: Keine solche Datei oder kein solches Verzeichnis

Frage

  • Wie kann man diese Fehler beseitigen?
  • Gibt es einen besseren Weg, um die Ziele zu erreichen als das in meinem Code?

Danke im Voraus!

Mike VDC
quelle
4
+1 für eine fast funktionierende Lösung!
user5325
Dies ist keine Antwort auf Ihre spezielle Frage, aber Sie können diff -rq DIR1 DIR2damit nicht nur die Verzeichnisstruktur, sondern auch den Dateiinhalt vergleichen.
jrw32982 unterstützt Monica

Antworten:

10

Der bessere Weg

Analysiere nichtls ; Verwenden Sie stattdessen Globs. Tatsächlich verwenden Sie bereits Globs, wickeln sie einfach ein ls, was sinnlos ist. Sie müssen nur nullglobeingeschaltet sein, wenn keine Übereinstimmungen vorliegen.

Auch das Vermeiden cdvereinfacht die Dinge.

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

Ausgabe:

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/

Ich habe hinzugefügt B/child-3-something, um die sekundäre Anforderung zu testen. Dadurch wird die Verzeichnisstruktur zum Testen erstellt:

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}

By the way, ShellCheck ist sehr nützlich für die Probleme in Shell - Skripten zu finden.

wjandrea
quelle
ShellCheck.net ist interessant. Wissen Sie, ob es alles auf seine eigenen Server hochlädt oder ob alles lokal durchgeführt wird? Ich wundere mich nur über die Privatsphäre der eingegebenen Informationen. [Die Installation des shellcheckPakets wäre am sichersten]
Xen2050
@ Xen2050 Ich habe gerade versucht, mein Internet auf der Website auszuschalten, und es scheint hochgeladen zu werden. Ich würde mir vorstellen, dass es nicht hält, aber nicht sicher. Und ja, das Paket ist gut; Ich benutze ein Atom-Plugin , das es benutzt.
Wjandrea
Danke für die Vorschläge. Und auch vielen Dank für den Hinweis ShellCheck. Ich habe den Teil geliebt, in dem es Ihnen nicht nur Ihre Fehler sagt, sondern auch Vorschläge gibt! @ Xen2050, über den Hochladen Teil, Ich habe gerade shellcheckmit aptbehindertem Netzwerk und dann. Es scheint ohne Internet zu funktionieren .
Mike VDC
2

Wenn Sie lseinen nicht vorhandenen Ordner aufrufen, wird die aufgetretene Fehlermeldung ausgegeben. Der einfache Weg ist, dies einfach zu ignorieren, indem Sie Zeile 5 in Ihrem Skript durch Folgendes ersetzen : new_dirs=(`ls -1d $f* 2> /dev/null`);.

Cauon
quelle
Hast du das getestet? Stderr scheint standardmäßig ignoriert zu werden. Wenn ich t=(`echo ok; echo err 1>&2`)$ t ausführe (oder ${t[@]}) nur ok enthält, wird err im Terminal angezeigt, aber trotzdem nicht gespeichert. Oder hat mein Test etwas Lustiges?
Xen2050