Forum: PC-Programmierung bash: finde Dateien mit gleichem Namen aber anderer Extension


von Martin (Gast)


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

von MaWin (Gast)


Lesenswert?

Martin schrieb:
> 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)

Also ist es immer .jpg.txt -> .jpg? Oder kann es auch mal anders sein?

von korn13 (Gast)


Lesenswert?

unter Linux gibts das Tool mmv
(In Win Umgebungen via cygwin auch verfügbar)
… es braucht gar kein bash Skript drumherum

von Martin (Gast)


Lesenswert?

Hallo.

Ich habe immer ein Paar: also 1.jpg und 1.jpg.txt
Davon jede Menge mit zT langen Dateinamen die auch zB einen. enthalten.
Immer ein Paar will ich umbenennen in zB a.jpg und a.jpg.txt


Martin

von Zocker_62 (Gast)


Lesenswert?

Was soll eigentlich nach "z" kommen?

Ist sber eine mehr rhetorische Frage.
Ich arbeite nicht für lau.

von Modeberater für Ingenieure (Gast)


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.

von Modeberater für Ingenieure (Gast)


Lesenswert?

Add:
Das Script muss im dir liegen wo die entspr. dir1 dir2,... liegen.

von Modeberater für Ingenieure (Gast)


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.

von Modeberater für Ingenieure (Gast)


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.

von Norbert (Gast)


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

von Modeberater für Ingenieure (Gast)


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

von Modeberater für Ingenieure (Gast)


Lesenswert?

Add zu Script 2:
Das
1
names=( "a" "b" "c" "d" )
2
index=0
ist ein Überbleibsel aus der ersten Version, das kann man rauslöschen, 
wird nicht gebraucht.

von Modeberater für Ingenieure (Gast)


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.

von DPA (Gast)


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.

von foobar (Gast)


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

von foobar (Gast)


Lesenswert?

> untested

Mist... mindestens ein " fehlt :-/

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.