staging.inyokaproject.org

Dateien in einem Ordner nach dem Ordner bennenen

Status: Gelöst | Ubuntu-Version: Server 20.04 (Focal Fossa)
Antworten |

Fisch133

Anmeldungsdatum:
20. Dezember 2021

Beiträge: 6

Hallo zusammen,

ich versuche gerade ein Script zu erstellen, welches Dateien in einem Ordner nach dem Ordner benennt und diese in einen separaten Ordner kopiert. Also so:

  • Ordnername 1

    • Datei 1

  • Ordnername 2

    • Datei 2

  • ...

–>

  • Ordner 3

    • Ordnername 1 (Vorher Datei 1)

    • Ordnername 2 (Vorher Datei 2)

Leider habe ich keine Ahnung von der Shell würde es aber (in Worten) ungefähr so lösen:

  • For oder While Schleife (Solange bis keine Ordner mehr in dem Verzeichnis sind)

  • Suche nach einem Ordner

  • Kopiere den Namen des Ordners in eine "Variable"

  • Suche nach einer Datei in diesem Ordner

  • Bennene die Datei nach der "Variable" um

  • Kopiere die Datei nach "Ordner 3"

Jetzt bräuchte ich das nur noch für die Shell. Kopieren und umbennenen geht mit "mv". Mehr weiß ich aber auch nicht 😕

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1303

Hallo Fisch123,

was auch immer dich dazu bewegt hat, die Directory-Strukur flach zu klopfen, aber wie im folgenden Beispiel kannst du das machen.

$ cd src
$ find -type f | while read i; do n=${i//\//\\};cp "$i" ../dst/"${n:2}"; done
$ ls -1R ../dst
../dst:
'dir1\dir11\file1'
'dir1\dir11\file2'
'dir1\file1'
'dir2\dir21\file1'
'dir2\dir23\file1'
'dir2\dir23\file2'
'dir2\dir23\file3'
'dir2\file1'
file1
$ ls  -R
.:
dir1  dir2  file1

./dir1:
dir11  dir12  file1

./dir1/dir11:
file1  file2

./dir1/dir12:

./dir2:
dir21  dir22  dir23  file1

./dir2/dir21:
file1

./dir2/dir22:

./dir2/dir23:
file1  file2  file3

Statt des "\" als Verzeichnistrenner kannst du natürlich auch ein anderes Zeichen verwenden. Willst du da ein "+" haben, wäre oben statt "n=${i//\//\\}" der Eintrag "n=${i//\//+}" zu wählen.

Fisch133

(Themenstarter)

Anmeldungsdatum:
20. Dezember 2021

Beiträge: 6

Hi Shiro,

vielen Dank für die (fast)Lösung.

Jetzt heißen die Dateien Ordnername1\Datei1 im Zielverzeichnis.

Der Dateiname soll aber durch den Ordnernamen ersetzt werden und nicht ergänzt.

Also Datei1 im Ordner1 soll dann Ordner1 heißen.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Meinst du sowas?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3
import pathlib
import argparse

parser = argparse.ArgumentParser(description='rename one file after the directory it is in')
parser.add_argument('source_dirs', metavar='SOURCEDIRS', nargs='+',
                    help="Directories to search for files")
parser.add_argument('-t', '--target', matavar="TARGET", required=True, help='target directory')
args = parser.parse_args()


for directory in (pathlib.Path(d) for p in args.source_dirs):
    if not directory.is_dir:
        continue
    try:
        filepath = next(f for f in pathlib.Path(directory).iterdir() if f.is_file())
    except StopIteration:
        echo f"warning: {directory} does not contain a file"
    extensions = filepath.suffixes
    filepath.rename(args.target / f"{directory.name}{''.join(extensions)}"

Das macht dann so etwas:

$ tree
.
├── dest
├── flatten_dirs.py
└── source
    ├── Ordner1
    │   └── Datei.txt
    ├── Ordner2
    │   └── Datei.txt
    ├── Ordner3
    │   └── Datei.txt
    └── Ordner4

6 directories, 4 files
$ python flatten_dirs.py source -t dest
$ tree
.
├── dest
│   ├── Ordner1.txt
│   ├── Ordner2.txt
│   └── Ordner3.txt
├── flatten_dirs.py
└── source
    ├── Ordner1
    │   └── Datei.txt
    ├── Ordner2
    │   └── Datei.txt
    ├── Ordner3
    │   └── Datei.txt
    └── Ordner4

6 directories, 7 files 

Fisch133

(Themenstarter)

Anmeldungsdatum:
20. Dezember 2021

Beiträge: 6

Ja genau. Das Scriptschnipsel mit dem umkopieren der Dateien soll allerdings noch in ein größeres Script mit eingefügt werden, da die Dateien noch weiter verarbeitet werden müssen. Der weiterverarbeitende Teil ist schon fertig und in Bash. Dein Script läuft darunter leider nicht, da ja python. Oder kann man python und bash in einem Script verwenden?

Ansonsten ist shiros Lösung mit

1
find -type f | while read i; do n=${i//\//\\};cp "$i" ../dst/"${n:2}"; done

schon sehr nah dran. Müsste wohl hier etwas geändert werden, damit der Dateiname nicht mitkopiert wird: n=${i//\//\\}

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Ups, da ist mir eine frühere Fassung in den obigen Post reingerutscht - das Python-Skript sollte so aussehen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
from pathlib import Path
import argparse
import shutil

parser = argparse.ArgumentParser(description='rename one file after the directory it is in')
parser.add_argument('source_dirs', metavar='SOURCEDIRS', nargs='+',
                    help="Directories to search for files")
parser.add_argument('-t', '--target', metavar="TARGET", required=True, help='target directory')
args = parser.parse_args()


for directory in (Path(d) for d in args.source_dirs):
    if not directory.is_dir:
        continue
    used_dirs = set()
    for filepath in (f for f in Path(directory).rglob('*') if f.is_file()):
        if filepath.parent in used_dirs:
            print(f"Warning: {filepath.parent} has more than one file, skipping")
            continue
        extensions = filepath.suffixes
        shutil.copy2(filepath, Path(args.target) / f"{filepath.parent.name}{''.join(extensions)}")
        used_dirs.add(filepath.parent)

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11278

Fisch133 schrieb:

Oder kann man python und bash in einem Script verwenden?

Man kann z.B. ein Heredoc nutzen, um ein Python-Skript in einem Bash-Skript unterzubringen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
sourcedir="source"
destdir="dest"

python3 - "$sourcedir" -t "$destdir" <<__SCRIPT__
from pathlib import Path
import argparse
import shutil

parser = argparse.ArgumentParser(description='rename one file after the directory it is in')
parser.add_argument('source_dirs', metavar='SOURCEDIRS', nargs='+',
                    help="Directories to search for files")
parser.add_argument('-t', '--target', metavar="TARGET", required=True, help='target directory')
args = parser.parse_args()


for directory in (Path(d) for d in args.source_dirs):
    if not directory.is_dir:
        continue
    used_dirs = set()
    for filepath in (f for f in Path(directory).rglob('*') if f.is_file()):
        if filepath.parent in used_dirs:
            print(f"Warning: {filepath.parent} has more than one file, skipping")
            continue
        extensions = filepath.suffixes
        shutil.copy2(filepath, Path(args.target) / f"{filepath.parent.name}{''.join(extensions)}")
        used_dirs.add(filepath.parent)
__SCRIPT__

shiro Team-Icon

Supporter

Anmeldungsdatum:
20. Juli 2020

Beiträge: 1303

Wenn du die Dateinamen raus nimmst, und mehrere Dateien gleichen Typs in dem Verzeichnis hast, wirst du Dateien verlieren. Nun denn:

find $srcDir/ -type f -printf "%P\n" | while read i; do cp "$srcDir/$i" "$dstDir/${i%/*}.${i#*.}"; done

Wenn in dem Verzeichnis Dateien vom Type "Dir1/file1.a.b" sind, würde "Dir1.a.b" erzeugt werden. Wenn du nur "Dir1.b" haben willst, musst du im obigen Befehl

statt : ${i#*.}
besser: ${i##*.}

verwenden.

Fisch133

(Themenstarter)

Anmeldungsdatum:
20. Dezember 2021

Beiträge: 6

In den Ordnern ist jeweils nur eine Datei vorhanden. Daher sollten keine Dateien den gleichen Namen bekommen.

Vielen Dank euch. Habt mir sehr geholfen und spart mir in Zukunft viel Arbeit das (manuell) umzubenennen.

Antworten |