it-swarm.com.de

Erstellen Sie eine separate Textdatei, um den Inhalt in jedem Verzeichnis und Unterverzeichnis aufzulisten

Ich habe einen Stammordner und darin gibt es viele Verzeichnisse und Dateien. Ich muss eine Liste der Inhalte in jedem Unterverzeichnis mit dem Namen list.txt speichern.

Angenommen, ich habe

A
|
-----B
|    |---Z
|    |---a.txt
|    |---b.jpg
|
|
|----C
|    |--a.txt
|    |--b.txt

Wenn Sie den Befehl ausführen, sollte in jedem Unterverzeichnis ein list.txt mit durch Kommas getrennten Inhalten stehen.

Ich habe # kommentiert, was der Inhalt sein soll ...

A
|
-----B
|    |---Z
|    |---a.txt
|    |---b.jpg
|    |---list.txt  # Z,a.txt,b.jpg
|
|
|----C
|    |--a.txt
|    |--b.txt
|    |--list.txt  # a.txt,b.txt

Am ehesten könnte ich die Dateien auflisten

find . -maxdepth n -type f -printf '%f\n'

ich weiß aber nicht, wie ich den Inhalt separat speichern soll.

Bitte schlagen Sie etwas vor.

4
Derek James

Das folgende Skript fügt eine Liste zu allen Unter - Verzeichnissen aus einem Verzeichnis rekursiv hinzu:

#!/usr/bin/env python3
import os
import sys

for root, dirs, files in os.walk(sys.argv[1]):
    for dr in dirs:
        dr = os.path.join(root, dr)
        open(os.path.join(dr, "list.txt"), "wt").write(
            ",".join(f for f in os.listdir(dr) if f != "list.txt")
            )

Benutzen

  1. Kopieren Sie das Skript in eine leere Datei und speichern Sie es als dirlists.py
  2. Führen Sie es mit dem Hauptverzeichnis als Argument aus:

    python3 /path/to/dirlists.py /path/to/maindirectory
    

Hinweis

Wie bereits erwähnt, fügt das Skript allen Unterverzeichnissen eine list.txt hinzu. Wenn Sie auch eine Liste im Hauptverzeichnis (root) Ihres Verzeichnisses benötigen oder haben möchten, geben Sie dies bitte an.

Erläuterung

  1. Alle Verzeichnisse in einem Verzeichnis rekursiv auflisten (durchgehen):

    for root, dirs, files in os.walk(sys.argv[1]):
        for dr in dirs:
            dr = os.path.join(root, dr)
    
  2. Erstellen Sie für jeden von ihnen eine Inhaltsliste:

    os.listdir(dr)
    
  3. Öffnen Sie die Textdatei (erstellen Sie sie, falls erforderlich) und schreiben Sie den aufgelisteten Inhalt , wobei mögliche frühere Dateien mit dem Namen list.txt ausgeschlossen sind:

    open(os.path.join(dr, "list.txt"), "wt").write(
        ",".join(f for f in os.listdir(dr) if f != "list.txt")
        )
    

BEARBEITEN

Wie in einem Kommentar gefordert:

Wenn Sie die Zeile in list.txt mit einem Komma beenden müssen, ersetzen Sie einfach:

",".join(f for f in os.listdir(dr) if f != "list.txt")

durch:

",".join(f for f in os.listdir(dr) if f != "list.txt")+","

Beachten Sie die Einrückung, platzieren Sie den Ersatz an genau derselben Position

5
Jacob Vlijm

Um es rekursiv zu machen, schalten Sie zuerst globstar ein.

shopt -s globstar

Anschließend können Sie im übergeordneten Verzeichnis (A in Ihrer Struktur) Folgendes ausführen:

for d in **; do [[ -d "$d" ]] && (find "$d" -mindepth 1 -maxdepth 1 \( -not -name "list.txt" \) -printf '%f,' | sed 's/,$/\n/') |tee "$d"/list.txt ; done

oder etwas besser lesbar

for d in **; do 
  [[ -d "$d" ]] && 
  (find "$d" -mindepth 1 -maxdepth 1 \( -not -name "list.txt" \) -printf '%f,' | sed 's/,$/\n/') | tee "$d"/list.txt
done

welches, wenn Verzeichnis a enthält

├── a
│   ├── 1.txt
│   ├── 2.txt
│   ├── 3.txt
│   ├── a badly named file &
│   └── Z

erstellt eine Liste im Verzeichnis a, die so aussieht:

2.txt,1.txt,3.txt,Z,a badly named file &

find erzeugt keine geordnete Ausgabe. Wenn das also ein Problem ist, muss ich mir einen besseren Weg überlegen. Der \( -not -name "list.txt" \) im find Ausdruck soll verhindern, dass sich die Liste selbst einschließt, und der sed Ausdruck dient lediglich zum Entfernen des nachgestellten Kommas. Schade um all diese zusätzlichen Bytes.

Möglicherweise möchten Sie globstar deaktivieren, wenn Sie fertig sind

shopt -u globstar
4
Zanna

Einzeiler-Version

Verwenden Sie find, um zuerst Verzeichnisse abzurufen, dann erledigt Shell die Arbeit für Sie:

$ tree                                                                                                                                                
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   └── mv-files.py
└── another_dir
    ├── {file}1
    └── {file}2

3 directories, 4 files

$  find -type d -exec bash -c 'cd $1; find  -maxdepth 1  -not -name "." -not -name "list.txt" -printf "%f," | awk "{print substr(\$0,0,length(\$0)-1)}"  > list.txt' bash "{}" \;

$ cat a_directory/list.txt                                                                                                                            
mv-files.py,a_file,list.txt,a_subdir

So funktioniert das:

  • wir verwenden den Befehl find mit -type d, um alle Verzeichnisse herauszufiltern
  • Die Anweisung -exec mit dem Terminator \; ermöglicht es uns, den angegebenen Befehl für jedes Argument auszuführen, das find erhält
  • in -exec führen wir bash aus, wobei -c das Flag enthält, an das wir $0 das Argument bash und $1 das Verzeichnis übergeben, in dem der Outter find lokalisiert
  • bash gibt das angegebene Verzeichnis ein und verwendet find mit dem Argument - maxdepth 1, um den Befehl nur auf dieses Unterverzeichnis zu beschränken. -not -name "." schließt den . -Verzeichnislink aus, der auf self verweist.
  • Als nächstes übergeben wir den Text an awk, der nur dazu dient, den letzten von find angegebenen , zu entfernen, sodass wir eine gültige CSV-Liste haben. Beachten Sie die Verwendung von Anführungszeichen und \$. Dies soll das Zitieren vereinfachen und verhindert, dass Bash $0 als eigene Positionsargumente interpretiert, sondern als awk -Befehl.
  • Die gesamte Liste der Elemente, die in find enthalten sind, wird über die Weiterleitung list.txt an > gesendet.

Eine weitere Verbesserung könnte darin bestehen, -not -name "list.txt" innerhalb des inneren find Befehls zu verwenden, um die Listendatei selbst auszuschließen (da > immer die Datei erstellt, die zuerst geschrieben werden soll, list.txt erscheint auch in der Liste).

Wenn ich dies für mich selbst tun würde, würde ich die Liste der Dateien mit dem Trennzeichen \0 schreiben, um den Umgang mit schwierigen Dateinamen zu vermeiden. Dies erfordert jedoch auch, dass Sie sich daran erinnern, dass list.txt in \0 enthalten ist. Formatieren und Schreiben einer Parser-Funktion dafür.

Vollständige Skriptversion

Aus Gründen der Lesbarkeit ist hier eine vollständige Skriptversion anstelle eines Einzeilers.

Skript:

#!/bin/bash
# note : this assumes you run the script from top-most directory
find  -type d  | while IFS= read -r directory;
do
    cd "$directory"
    find  -maxdepth 1  -not -name "." -not -name "list.txt" -printf "%f," |
    awk "{print substr(\$0,0,length(\$0)-1)}"  > list.txt
    cd - > /dev/null
done

Beachten Sie, dass dieses Skript aus dem obersten Verzeichnis ausgeführt wird. Es nimmt auch self in die Liste auf, wenn es im selben Verzeichnis gespeichert ist. Wenn Sie es beispielsweise in ~/bin (oder in ein anderes Verzeichnis, das zur $PATH -Variablen gehört) platzieren und ausführen, wird der Skriptname nicht in der Liste angezeigt.

Prüfung :

$ tree                                                                                                                                                
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   └── mv-files.py
├── another_dir
│   ├── {file}1
│   └── {file}2
└── make_lists.sh

3 directories, 5 files

$ ./make_lists.sh                                                                                                                                     

$ tree
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   │   └── list.txt
│   ├── list.txt
│   └── mv-files.py
├── another_dir
│   ├── {file}1
│   ├── {file}2
│   └── list.txt
├── list.txt
└── make_lists.sh

3 directories, 9 files

$ cat a_directory/list.txt                                                                                                                            
mv-files.py,a_file,a_subdir
3