Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > de.comp.os.unix.shell > #14705

Re: copy mit exclude?

From Helmut Waitzmann <nn.throttle@erine.email>
Newsgroups de.comp.os.unix.shell
Subject Re: copy mit exclude?
Date 2026-05-11 11:11 +0200
Organization A noiseless patient Spider
Message-ID <83se7ytint.fsf@helmutwaitzmann.news.arcor.de> (permalink)
References <10tlgq0$2pc6$1@yggdrasil.dn.cgarbs.de>

Show all headers | View raw


 Christian Garbs <mitch@cgarbs.de>:
> Mahlzeit!
>
> Ich habe ein einfach gestricktes Backup-Skript, bei dem
> ich unter anderem Verzeichnisse rekursiv in ein
> Arbeitsverzeichnis kopiere und anschließend dort nochmal
> gezielt Dateien lösche, die ich nicht im Backup haben
> will.
>
> Jetzt ist es auf einem Rechner so, dass ich erst knapp 4
> GB rekursiv kopiere, um dann anschließend 3,5 GB davon
> wieder zu löschen.
>
> Das ist ineffizient und soll anders werden, daher suche
> ich nach einer Methode, bereits beim Kopieren die Dinge
> auszuschließen, die ich nicht im Backup haben will. In
> meinem Fall sind das verschiedene Unterverzeichnisse.
>
>
> Mir sind bisher zwei Varianten eingefallen:
>
> - tar c $source | ( cd $target; tar x)
>   mit passenden Excludes auf der Sender-Seite
>
> - rsync kann auch von lokal nach lokal kopieren
>   und unterstützt ebenfalls Excludes
>
> In beiden Fällen stehe ich mit der Angabe der Excludes
> etwas auf Kriegsfuß, aber das kriege ich mit
> Manpage-Studium und Ausprobieren schon hin ;-)
>

 Ich habe bisher nie mit excludes bei „tar“ herumgemacht, sondern 
 immer dann, wenn ich gewisse Teile eines Dateiwalds davon 
 ausschließen wollte, ins Tar‐Archiv zu gelangen, die Menge der zu 
 archivierenden Dateien mit 


   find … -exec printf '%s\0' '{}' +


 (mit GNU‐„find“ auch einfacher:) 


   find … -print0


 bestimmt und anschließend an GNU‐„tar“ mit den „tar“‐Optionen 
 „--no-recursion“, „--null“ und „--files-from“ verfüttert: 


   find … -print0 |
   tar c --no-recursion --null --files-from=-


 Das ist robust gegenüber allen möglichen Dateinamen – auch 
 solchen, die undruckbare Zeichen (line feeds, spaces, …) 
 enthalten – und gibt mir mittels „find“ volle Kontrolle darüber, 
 welche Dateien ins Archiv kommen und welche nicht. 


 Die Option „--no-recursion“ bewirkt, dass Verzeichnisse, die man 
 „tar“ beim Aufruf angibt, nicht automatisch die in ihnen 
 enthaltenen Dateinamen nachziehen.  Man muss dann „tar“ wirklich 
 jeden zu archivierenden Dateinamen einzeln nennen.  Genau das 
 kann aber „find“ tun. 


> Randbedingungen:
>

[…]

> - file attributes (chattr) brauchen _nicht_ berücksichtigt zu werden:
>   das finale Backup-Ziel ist ein tar, tar unterstützt das nicht und
>   deshalb lasse ich ein Skript rekursiv über den ganzen Rechner
>   rödeln, sammele die gesetzen chattr-Attribute ein und schreibe sie
>   in eine Textdatei, die ebenfalls im Backup landet.
>
>   Bash-Schnipsel dazu (solange path keine Leerzeichen enthält, ginge
>   das auch ohne Bash und ohne Array):
>
>     declare -a prune
>     for path in /dev /mnt/remote-1 /mnt/remote-2 /proc /run /sys /tmp; do
>         prune+=(-path "$path" -prune -o)
>     done
>     find / "${prune[@]}" -exec lsattr -d {} + 2>/dev/null | grep -v ^---------------------- > "$WORKDIR"/files_with_chattrs

 Anmerkungen dazu: 


 Mein Newsreader fragt mich wegen der vorangegangenen zitierten 
 langen Zeile, ob er das Posting wirklich losschicken soll.  Wie 
 man Shell‐Skripte so umbricht, dass sie (allermeist) mit kurzen 
 Zeilen auskommen, ist im Folgenden zu sehen. 


 Es ist generell eine gute Idee, zu verarbeitende Dateinamen nicht 
 mit absolutem Pfad sondern statt dessen mit relativem Pfad 
 (bezogen auf das Arbeitsverzeichnis „/“) zu erstellen:  Das 
 erlaubt es, die Dateinamen auch auf andere Dateibäume als den des 
 „/“‐Verzeichnisses, beispielsweise auf das Backup‐Verzeichnis, 
 anzuwenden (indem man das Arbeitsverzeichnis entsprechend wählt). 


 Auch ohne Bash darf path Leerzeichen (und andere nicht‐druckbare 
 Zeichen) enthalten, wenn man die positional parameters des 
 POSIX‐Standards nutzt.


   (
     cd / &&
     set -- &&
     for path in ./dev ./mnt/remote-1 ./mnt/remote-2 \
       ./proc ./run ./sys ./tmp
     do
       set -- "$@" -path "$path" -prune -o
     done &&
     find . "$@" -exec lsattr -d -- {} + 2>/dev/null
   ) |
   grep -v -e '^----------------------' \
     > "$WORKDIR"/files_with_chattrs


 Ein Problem daran, mit „lsattr“ eine Datei zusammenzustellen, die 
 Dateinamen mit je einem Line‐Feed‐Zeichen getrennt enthält, ist, 
 dass Dateinamen auch Line‐Feed‐Zeichen enthalten können.  
 Speziell bei einem Dateiarchiv, das Unmengen von Dateien, deren 
 Namen man nicht unter Kontrolle hat, aufnehmen soll, ist das ein 
 Problem, das zu Sicherheitslücken führen kann. 


 Um das zu vermeiden, bleibt nichts anderes übrig, als „lsattr“ 
 für jeden Dateinamen einzeln aufzurufen und von der jeweils 
 erhaltenen Ausgabe alle Zeichen hinter den Attributen (also den 
 Dateinamen) wegzuwerfen. (Ja, das sind sehr viele Aufrufe, ist 
 aber mit „lsattr“, das keine Möglichkeit bietet, Dateinamen auf 
 unmissverständliche Weise auszugeben, nicht anders zu machen.) 


 Danach muss noch der Dateiname ausgegeben und mit einem 
 ASCII‐NUL‐Zeichen (das ist das einzige Zeichen, das in Dateinamen 
 nicht vorkommen kann und sich deshalb als Dateinamenstrenner 
 eignet) abgeschlossen werden: 


   printf '%s\0' "$dateiname"


 Bei so einem Skript, das nahezu den kompletten Dateibaum abgrast, 
 kann es vorkommen, dass Dateinamen Bytefolgen enthalten, die im 
 augenblicklich eingestellten Locale keine gültigen (Multi‐Byte‐) 
 Zeichen darstellen.  Das kann beispielsweise bei dem 
 „find“‐Prädikat „-path“ Ärger geben.  Daher ist es ratsam, als 
 Locale „C“ oder „POSIX“ einzustellen.  Das kriegt man am 
 einfachsten hin, indem man die Umgebungsvariable „LC_ALL“ 
 entsprechend setzt. 


 In der Parameterliste für „find“ sieht alles zusammen dann so 
 aus: 


   (
     cd / &&
     set --
     for path in ./dev ./mnt/remote-1 ./mnt/remote-2 ./proc \
       ./run ./sys ./tmp
     do
       set -- "$@" -path "$path" -prune -o
     done &&
     LC_ALL=C find . "$@" \
       -exec sh -c '
         set_exitstatus { true ; }
         for dateiname
         do
           if ! attribute="$( lsattr -d -- "$dateiname" )"
           then
             # Merke dir, dass ein Fehler aufgetreten ist,
             set_exitstatus { false ; }
             # ... und geh weiter zur naechsten Datei:
             continue
           fi
           attribute=${attribute%%[![:graph:]]*}
           # "$attribute" enthaelt jetzt nur noch die Attribute
           # ohne den Dateinamen.

           # Wenn der Inhalt der Variablen "$attribute" nur aus
           # Minuszeichen besteht, wird er geleert:
           attribute=${attribute%"${attribute##*[!-]*}"}
           #
           # => Genau dann, wenn die Datei keine Attribute hat,
           # ist "$attribute" die leere Zeichenkette.

           if ${attribute:+:} false
           then
             # "$attribute" ist eine nicht-leere Zeichenkette.
             #
             # Gib die Attribute, gefolgt von einem
             # Leerzeichen, dem Dateinamen und einem
             # NUL-Zeichen, aus:
             #
             printf %s %s\\0 "$attribute" "$dateiname"
           fi
         done
         set_exitstatus
         ' sh '{}' +
   ) > "$WORKDIR"/files_with_chattrs


 Bemerkungen: 


  Falls beim Versuch, von einer Datei die Attribute zu erfragen, 
  ein Fehler auftritt, liefert das „find“‐Kommando einen 
  Exit‐Status ungleich 0.  (Trotzdem bricht das Kommando aber 
  nicht ab, sondern lässt den den Fehler hervorrufenden Dateinamen 
  aus und fährt mit den weiteren Dateien fort.) 


  Die Ausgabe dieses Kommandos besteht dann aus 0 oder mehr 
  Zeichenfolgen der Form („\0“ steht für ein NUL‐Zeichen) 


  Attribute Dateiname\0


  Sie können beispielsweise mit dem „xargs“‐Kommando 


  xargs -0 -r -x -- sh -c '
    for ad
    do
      attribute=${ad%% *}
      dateiname=${ad#* }
      …
    done
    ' sh < "$WORKDIR"/files_with_chattrs


 verarbeitet werden. 

Back to de.comp.os.unix.shell | Previous | NextPrevious in thread | Next in thread | Find similar


Thread

copy mit exclude? Christian Garbs <mitch@cgarbs.de> - 2026-05-08 20:24 +0000
  Re: copy mit exclude? ram@zedat.fu-berlin.de (Stefan Ram) - 2026-05-08 21:18 +0000
    Re: copy mit exclude? Ulli Horlacher <framstag@rus.uni-stuttgart.de> - 2026-05-08 21:40 +0000
  Re: copy mit exclude? Ulli Horlacher <framstag@rus.uni-stuttgart.de> - 2026-05-08 21:50 +0000
  Re: copy mit exclude? Tim Landscheidt <tim@tim-landscheidt.de> - 2026-05-08 22:04 +0000
    Re: copy mit exclude? Christian Garbs <mitch@cgarbs.de> - 2026-05-09 16:39 +0000
      Re: copy mit exclude? Ulli Horlacher <framstag@rus.uni-stuttgart.de> - 2026-05-09 18:39 +0000
        Re: copy mit exclude? Christian Garbs <mitch@cgarbs.de> - 2026-05-09 23:19 +0000
          Re: copy mit exclude? Ulli Horlacher <framstag@rus.uni-stuttgart.de> - 2026-05-10 07:20 +0000
          Re: copy mit exclude? Torsten Fleischmann <TorstenFleischmann@web.de> - 2026-05-10 11:04 +0200
  Re: copy mit exclude? Christian Weisgerber <naddy@mips.inka.de> - 2026-05-08 21:50 +0000
  Re: copy mit exclude? Thomas Noll <-_tn_-@web.de> - 2026-05-08 22:31 +0000
    Re: copy mit exclude? Christian Garbs <mitch@cgarbs.de> - 2026-05-09 12:05 +0000
  Re: copy mit exclude? Helmut Waitzmann <nn.throttle@erine.email> - 2026-05-11 11:11 +0200
  Re: copy mit exclude? Christian Garbs <mitch@cgarbs.de> - 2026-05-12 22:05 +0000

csiph-web