Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > de.comp.os.unix.shell > #14705
| 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> |
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 | Next — Previous in thread | Next in thread | Find similar
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