Vergleichen Sie den Verzeichnisinhalt rekursiv nach Namen und ignorieren Sie dabei die Dateierweiterungen

7

Ich habe ein Verzeichnis mit ungefähr 7.000 Musikdateien. Ich habe lame verwendet, um alle darin enthaltenen Dateien rekursiv in ein separates Verzeichnis umzucodieren und alle Dateien mit demselben relativen Pfad und Dateinamen auszugeben. Die Ausgabedateien haben die Erweiterung .mp3, aber einige der Eingabedateien hatten unterschiedliche Erweiterungen (.wma, .aac usw.).

Ich kann sehen, dass im Ausgabeverzeichnis ein Unterschied in der Anzahl der Dateien von ~ 100 Dateien fehlt. Ich möchte einen Vergleich der beiden Verzeichnisse durchführen und eine Liste der Dateien abrufen, die in der Quelle, aber nicht im Ziel vorhanden sind. Dies wäre einfach genug, außer ich muss Unterschiede in der Dateierweiterung ignorieren.

Ich habe versucht, rsync mit aktiviertem Trockenlauf zu verwenden, konnte jedoch keine Möglichkeit finden, Dateierweiterungen zu ignorieren. Ich habe auch diff ausprobiert, konnte jedoch keine Option finden, um nur nach Namen zu suchen, aber Dateierweiterungen zu ignorieren. Ich begann zu denken, ich könnte einfach ein rekursives ls für beide Verzeichnisse erstellen, die Dateierweiterungen entfernen und dann die Ausgaben vergleichen, aber ich habe wirklich keine Ahnung, wo ich anfangen soll, die ls-Ausgabe mit sed oder awk zu ändern.

Robert S. Ciaccio
quelle

Antworten:

7

Um eine Liste anzuzeigen, gibt es zwei Varianten, eine, die in Unterverzeichnisse rekursiv ist, und eine, die dies nicht tut. Alle verwenden eine für bash, ksh und zsh spezifische Syntax.

comm -3 <(cd source && find -type f | sed 's/\.[^.]*$//' | sort) \
        <(cd dest && find -type f | sed 's/\.[^.]*$//' | sort)
comm -3 <(cd source && for x in *; do printf '%s\n' "${x%.*}"; done | sort) \
        <(cd dest && for x in *; do printf '%s\n' "${x%.*}"; done | sort)

Kürzer, in zsh:

comm -3 <(cd source && print -lr **/*(:r)) <(cd dest && print -lr **/*(:r))
comm -3 <(print -lr source/*(:t:r)) <(print -lr dest/*(:t:r))

Der commBefehl listet die Zeilen auf, die zwei Dateien ( comm -12) gemeinsam sind, die sich nur in der ersten Datei ( comm -23) oder nur in der zweiten Datei ( comm -13) befinden. Die Zahlen geben an, was von der Ausgabe abgezogen wird¹. Die beiden Eingabedateien müssen sortiert werden.

Hier sind die Dateien tatsächlich die Ausgabe eines Befehls. Die Shell wertet das <(…)Konstrukt aus, indem sie eine "gefälschte" Datei (ein FIFO oder einen /dev/fd/benannten Dateideskriptor) als Argument für den Befehl bereitstellt .

¹ Hier sind also die Minus-Sprüche völlig gerechtfertigt.


Wenn Sie Aktionen für die Dateien ausführen möchten, möchten Sie wahrscheinlich die Quelldateien durchlaufen.

cd source
for x in *; do
  set -- "…/dest/${x%.*}".*
  if [ $# -eq 1 ] && ! [ -e "$1" ]; then
    echo "$x has not been converted"
  elif [ $# -gt 1 ]; then
    echo "$x has been converted to more than one output file: " "$@"
  else
    echo "$x has been converted to $1"
  fi
done
Gilles 'SO - hör auf böse zu sein'
quelle
1
+1 besonders für die Fußnote (Bist du einer? Ich bin es.), Aber auch für die wie immer ausgezeichnete Antwort.
Bis auf weiteres angehalten.