bash: finde Dateien mit gleichem Namen aber anderer Extension

Gast #7166720
Lesenswert?

Hallo.

Ich brauche mal eine Idee:
Gegeben sind Unterorder in denen sich Dateien befinden nach folgendem 
Aufbau:
dir1\1.jpg            -> a.jpg
dir1\1.jpg.txt        -> a.jpg.txt
dir1\2.jpg            -> b.jpg
dir1\2.jpg.txt        -> b.jpg.txt
dir2\1.jpg            -> c.jpg
dir2\1.jpg.txt        -> c.jpg.txt
...

ich möchte jetzt via bash alle Dateien mit gleichem Namen (aber 
unterschiedlicher Extension) umbenennen ->

Wie kann ich durch alle Unterverzeichnisse loopen, dabei alle Datein mit 
gleichen Namen aber unterschiedlicher Extension finden (wie oben in dem 
Beispiel immer als Paar .jpg/ .jpg.txt und die ändert sich auch nicht) 
und dann dieses Paar umbenennen (da wollte ich uuidgen -r nehmen)?
Danke.


Martin
Gast #7166832
Lesenswert?

1
#!/bin/bash
2

3
set -u
4

5
# globbing ausschalten wenn dir leer
6
shopt -s nullglob
7

8
names=( "a" "b" "c" "d" )
9
index=0
10

11

12
# für debugging
13
#for i in ${!names[@]}
14
#do
15
#  echo "$i ${names[$i]}"
16
#done; exit
17

18
for verz in *
19
do
20
  if [ -d "$verz" ]
21
  then
22
    cd "$verz"
23
    for datei in *
24
    do
25
      if [ -f "$datei" ]; then
26
        name=$(echo "$datei"|grep -oP '^(.[^\.]*)')
27
        ext=$(echo "$datei"|grep -Po '\..*$')
28
        echo " $datei  -> ${names[$index]}${ext}"
29
        # mv "$datei"  "${names[$index]}${ext}"
30
       fi
31
    done
32
    cd ..
33
    ((index++))
34
  fi
35
done
36

37
shopt -u nullglob


Musst aber aufpassen dass die namen im Array auch entspr. mind. der 
Anzahl der Dirs entsprechen.
Gast #7166838
Lesenswert?

Add2:
Ein nullcheck auf $name und evt. $ext sollte man vorher auch noch machen
falls nix im dir ist, aber wenn du dir sicher bist dass da immer Dateien 
liegen kann man es weglassen.
Gast #7166843
Lesenswert?

Argh! Vergiss mein Script oben, ich habe mich nur auf dein Beispiel 
fixiert du willst alle gleiche Namen in einem Dir finden, das ist was 
anderes als das was mein Script macht,das mit uuidgen habe ich auch 
überlesen.
Gast #7166851
Lesenswert?

Martin schrieb:
> Hallo.
>
> Ich brauche mal eine Idee:
> Gegeben sind Unterorder in denen sich Dateien befinden nach folgendem
> Aufbau:
> dir1\1.jpg            -> a.jpg
> dir1\1.jpg.txt        -> a.jpg.txt
> dir1\2.jpg            -> b.jpg
> dir1\2.jpg.txt        -> b.jpg.txt
> dir2\1.jpg            -> c.jpg
> dir2\1.jpg.txt        -> c.jpg.txt
> ...
>
> ich möchte jetzt via bash alle Dateien mit gleichem Namen (aber
> unterschiedlicher Extension) umbenennen ->

Hier ist dein erster Gedankenfehler:
> dir1\1.jpg            -> a.jpg
›1‹ ist der Dateiname, ›jpg‹ die Erweiterung

> dir1\1.jpg.txt        -> a.jpg.txt
›1.jpg‹ ist der Dateiname, ›txt‹ die Erweiterung
Gast #7166859
Lesenswert?

1
#!/bin/bash
2
set -u
3

4
# globbing ausschalten wenn dir leer
5
shopt -s nullglob
6

7
names=( "a" "b" "c" "d" )
8
index=0
9

10
for verz in *
11
do
12
  if [ -d "$verz" ]
13
  then
14
    cd "$verz"
15
    newname=$(uuidgen -r)
16
    dateien=(*)
17
    echo "processing dir $verz..."
18
    for datei in "${dateien[@]}"; do
19
       if [ -f "$datei" ]; then
20
         name=$(echo "$datei"|grep -oP '^(.[^\.]*)')
21
         ext=$(echo "$datei"|grep -Po '\..*$')
22
         echo "name: $name ext:$ext"
23

24
         #weitere gleiche namen suchen
25
         #ineffizient aber geht
26
         for datei2 in "${dateien[@]}"; do
27
           name2=$(echo "${datei2}"|grep -oP '^(.[^\.]*)')
28

29
           ext2=$(echo "${datei2}"|grep -Po '\..*$')
30

31
           if [ "$name" = "$name2" ]; then
32
             echo "  doublette found: ${datei2}"
33
             echo "  mv $datei2 ${newname}${ext2}"
34
           fi
35
         done
36
       fi
37
    done
38
    cd ..
39
    echo
40
  fi
41
done
42
shopt -u nullglob
Gast #7166878
Lesenswert?

Verbesserungen:
* Die Direntries sotiert einlesen oder nach dem Einlesen sortieren.
Dann beim Doublettenprüfen der inneren for-loop vom entspr. index 
loslaufen,   dann ist die Laufzeit besser als O(n^2) wie oben. Wenn in 
einem dir sehr viele Dateien sind dauert das schon recht lange.

* Evt kann man auch grep durch das interne String matching von bash 
ersetzen das ist aber nicht so mächtig und reicht evt. nicht aus, geht 
dann aber schneller weil nicht das externe grep aufgerufen werden muss.

Also Vorlage reicht das Script oben ja erst mal.
Gast #7167332
Lesenswert?

Modeberater für Ingenieure schrieb:
> Die Direntries sotiert einlesen

Es ist schon Sortiert (ich glaube nach locale):
https://www.gnu.org/software/bash/manual/bash.html#Filename-Expansion
> After word splitting, unless the -f option has been set (see The Set
> Builtin), Bash scans each word for the characters ‘*’, ‘?’, and ‘[’.
> If one of these characters appears, and is not quoted, then the word
> is regarded as a pattern, and replaced with an alphabetically sorted
> list of filenames matching the pattern.

Noch ein Tip: Statt "cd x; zeug; cd .." kann man manchmal sub shells 
nutzen "( cd x; zeug; )". Mit sub shells kann man allerhand state 
isolieren. Variablen, das cwd, Dateiumleitungen, usw.
Gast #7167403
Lesenswert?

untested:
1
#!/bin/sh
2
txt="txt"
3
mvpairs() {
4
  for fn; do
5
    ext="${fn##*.}
6
    test -n "$ext" -a -f "$fn" -a -f "$fn.$txt" && {
7
      set -- $(md5sum "$fn")
8
      dfn="$(dirname "$fn")/$1.$ext"
9
      if test -f "$dfn"; then
10
        echo "dup: '$fn' already exists as '$dfn'"
11
      else
12
        echo "moving '$fn[.$txt]' -> '$dfn[.$txt]'"
13
        mv "$fn" "$dfn"
14
        mv "$fn.$txt" "$dfn.$txt"
15
      fi
16
    }
17
  done
18
}
19
#exec mvpairs "$@"
20
mvpairs */*.jpg

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren