Comparaison de dossiers

10

J'ai deux dossiers avec des structures de sous-dossiers similaires, que je voudrais comparer. Par exemple:

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

et

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

Je voudrais répertorier tous les sous-dossiers Adont le préfixe est un sous-dossier Bet répertorier également les sous-dossiers correspondants B. La sortie attendue est

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

Une exigence secondaire: s'il y a plusieurs correspondances B, cela devrait donner une erreur / un avertissement.

Ma solution :

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

Problème:

Pour ces valeurs de $f, qui n'ont pas de sous-dossiers correspondants B, la construction du tableau me donne une erreur. par exemple:

ls: impossible d'accéder à 'child-4 *': aucun fichier ou répertoire de ce type

Question

  • Comment se débarrasser de ces erreurs?
  • Existe-t-il un meilleur moyen d'atteindre le (s) but (s) que celui de mon code?

Merci d'avance!

Mike VDC
la source
4
+1 pour avoir fourni une solution presque fonctionnelle!
user5325
Ce n'est pas une réponse à votre question spécifique, mais vous pouvez utiliser diff -rq DIR1 DIR2pour comparer non seulement la structure du répertoire, mais le contenu du fichier.
jrw32982 prend en charge Monica

Réponses:

10

La meilleure façon

Ne pas analyserls ; utilisez plutôt des globes. En fait, vous utilisez déjà des globes, il vous suffit de les envelopper ls, ce qui est inutile. Vous avez juste besoin de l' nullgloballumer quand il n'y a pas de correspondance.

Éviter également cdsimplifie les choses.

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

Production:

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/

J'ai ajouté B/child-3-somethingpour tester l'exigence secondaire. Cela crée la structure de répertoires pour les tests:

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}

Soit dit en passant, ShellCheck est très utile pour trouver des problèmes dans les scripts shell.

wjandrea
la source
ShellCheck.net est intéressant, savez-vous s'il télécharge tout sur ses propres serveurs, ou est-ce que tout se fait localement? Je me questionne simplement sur la confidentialité des informations saisies. [L'installation du shellcheckpackage serait la plus sécurisée]
Xen2050
@ Xen2050 Je viens d'essayer de désactiver mon Internet sur le site, et il semble être téléchargé. J'imagine que ça ne le garde pas, mais je n'en suis pas sûr. Et oui, le paquet est bon; J'utilise un plugin Atom qui l'utilise.
wjandrea
Merci pour les suggestions. Et aussi des tonnes de remerciements pour pointer vers ShellCheck. J'ai adoré la partie où non seulement vous indique vos erreurs, mais donne également des suggestions! @ Xen2050, de la part de téléchargement, je viens d' installer à l' shellcheckaide aptet réseau désactivé. Il semble fonctionner sans Internet .
Mike VDC
2

Appeler lssur un dossier inexistant lance le message d'erreur que vous avez rencontré. Le plus simple est d'ignorer ce en remplaçant la ligne 5 dans votre script avec ceci: new_dirs=(`ls -1d $f* 2> /dev/null`);.

cauon
la source
L'avez-vous testé? Stderr semble être ignoré par défaut, lorsque je lance t=(`echo ok; echo err 1>&2`)$ t (ou ${t[@]}) ne contient que ok, l'erreur est vue dans le terminal mais pas enregistrée de toute façon. Ou y a-t-il quelque chose de drôle dans mon test?
Xen2050