Jak mogę filtrować zawartość pliku tar, tworząc kolejny plik tar w potoku?

13

Rozważ pojedynczy plik tar z zewnętrznego systemu, który zawiera niektóre katalogi z różnymi atrybutami, które chcę zachować, takie jak uprawnienia, czasy itp. Jak mogę łatwo wziąć podzbiór tych plików jako zwykły użytkownik (nie root)?

Szukasz czegoś takiego:

tar -f some.tar.gz --subset subdir/ | ssh remote@system tar xvz

Ważne jest również, aby zachować główne atrybuty (własność, grupa, tryb, mtime) w tym archiwum tar. Co z innymi atrybutami w pliku tar, takimi jak słowa kluczowe z rozszerzonym nagłówkiem ?

Punkty bonusowe za rozwiązanie, które pozwala uniknąć korzystania z katalogu tymczasowego na wypadek, gdyby ten podkatalog zawierał ogromne pliki.

Lekensteyn
źródło

Odpowiedzi:

14

bsdtar (oparty na libarchive) może filtrować tar (i niektóre inne archiwa) ze standardowego na standardowe. Może na przykład przechodzić tylko przez nazwy plików pasujące do wzorca i może s/old/new/zmieniać nazwę. Jest już zapakowany dla większości dystrybucji, na przykład jak bsdtarw Ubuntu.

sudo apt-get install bsdtar   # or aptitude, if you have it.

# example from the man page:
bsdtar -c -f new.tar --include='*foo*' @old.tgz
#create new.tar containing only entries from old.tgz containing the string ‘foo’
bsdtar -czf - --include='*foo*' @-  # filter stdin to stdout, with gzip compression of output.

Zauważ, że ma szeroki wybór formatów kompresji dla wejścia / wyjścia, więc nie musisz samodzielnie przepuszczać rur przez Gunzip / LZ4. Możesz użyć -dla stdin ze @tarfileskładnią i / lub -dla stdout jak zwykle.


Moje wyszukiwanie znalazło również to narzędzie do modyfikowania tar przesyłania strumieniowego, które wydaje się, że chcesz zdefiniować zmiany w archiwum za pomocą javascript. (Myślę, że całość jest napisana w js).

https://github.com/mafintosh/tar-stream

Peter Cordes
źródło
1
Doskonale, nie wiedziałem, że takie @original.tarpodejście jest możliwe dzięki bsdtar. Wydaje się, że działa również z rozszerzonymi atrybutami i kompresją </var/cache/pacman/pkg/libuv-1.7.0-1-x86_64.pkg.tar.xz bsdtar -czf - --include='usr/share/*' @- | tar tvz(i z jakiegoś powodu pusty wybór generuje serię zerowych bajtów, ale to nie jest dla mnie poważny problem).
Lekensteyn,
1
Według moich testów, s/old/new/ nie działa na plikach pochodzących ze starych archiwów przy użyciu @ old.tgz, działa tylko na prawdziwych plikach, archiwizując bezpośrednio z systemu plików. Szkoda, bo byłby to dla mnie najbardziej przydatny przypadek użycia.
Bart
4

Najprostszym sposobem byłoby skopiowanie całego archiwum; Zakładam, że nie chcesz tego robić, ponieważ jest za duży.

Zwykłe narzędzia wiersza poleceń ( tar, pax) nie obsługują kopiowania członków archiwum do innego archiwum.

Jeśli nie musiałeś zachowywać własności, sugerowałbym użycie systemów plików FUSE . Możesz użyć archivemount do zamontowania archiwum jako systemu plików; zrób to dla archiwum źródłowego i uruchom tar na zamontowanym systemie plików.

archivemount some.tar.gz mnt
cd mnt
tar -cz subdir | ssh example.com tar -xz
fusermount -u mnt

Alternatywnie możesz użyć AVFS :

mountavfs
cd ~/.avfs$PWD/some.tar.gz\#
tar -cz subdir | ssh example.com tar -xz

Alternatywnie możesz uruchomić taroryginalne archiwum i rozpakować je na zdalnym komputerze za pośrednictwem SSHFS .

sshfs example.com: mnt
cd mnt
tar -xf /path/to/some.tar.gz subdir
fusermount -u mnt

Jednak wszystkie te metody są uciążliwe, jeśli chcesz zachować własność. Wszystkie wymagają wypakowania do pliku na komputerze lokalnym, więc właścicielem tego pliku będzie musiała być zamierzona własność zdalna . Wymaga to działania jako root i może nie przynieść zamierzonego rezultatu, jeśli pliki są własnością kont o nazwach lub identyfikatorach różniących się między komputerem lokalnym a hostem zdalnym.

tarfileBiblioteka Pythona zapewnia dość łatwy sposób manipulowania elementami tar, dzięki czemu można je przetasować z jednego pliku tar do drugiego. Obsługuje standardowe formaty POSIX (ustar, pax), a także niektóre rozszerzenia GNU. Oto nieprzetestowany skrypt Pythona, który odczytuje plik tar (ewentualnie skompresowany za pomocą gzip lub bzip2) na standardowym wejściu i zapisuje plik tar skompresowany za pomocą bzip2 na standardowym wyjściu. Członkowie ze źródła są kopiowani, jeśli zaczynają od argumentu przekazanego do skryptu.

#!/usr/bin/env python2
import sys, tarfile
source = tarfile.open(fileobj=sys.stdin)
destination = tarfile.open(fileobj=sys.stdout, mode='w:bz2')
for info in source:
    if info.name.startswith(sys.argv[1]):
        destination.addfile(info)
destination.close()

Być wywoływanym jako

tar_filter <some.tar.gz subdir/ | ssh example.com tar -xj
Gilles „SO- przestań być zły”
źródło
1
bsdtar (oparty na libarchive) może filtrować archiwa tar w locie, zobacz moją odpowiedź.
Peter Cordes
Zadanie polegało na wyodrębnieniu danych z obrazu oprogramowania układowego, więc własność / członkostwo w grupie są naprawdę ważne. Metoda python może jednak działać.
Lekensteyn,
0

Alternatywnym podejściem bez przywilejów jest użycie fakerootprogramu do udawania, że ​​możesz zmienić własność. Podczas gdy inne atrybuty tar są tracone, zachowuje tryb, mtime i uid / gid. Te polecenia tworzą katalog tymczasowy, wyodrębniają podzbiór plików i na koniec tworzą nowe archiwum:

mkdir tmp
<some.tar.gz \
fakeroot -- sh -c 'cd tmp && tar -xzf- subdir/ && tar -czf- subdir' |
   ssh remote@system tar -xzvf-
rm -rf tmp
Lekensteyn
źródło
0

GNU tarma --deleteopcję:

$ tar -c a b c | tar --delete a | tar -t
b
c

W ten sposób można uzyskać podzbiór tar wejściowej, określając, co nie ma być uwzględniane w danych wyjściowych.

Niestety nie mogłem uzyskać --excludeopcji do pracy --delete, więc wydaje się, że najpierw musisz uzyskać jawną listę ( -t) rzeczy do usunięcia, a następnie przekazać ją do innego wywołania tar.

$ tar --delete --no-recursion `tar -t --exclude subdir <some.tar` <some.tar | ssh ...

Lub możesz zapisać listę w pliku zewnętrznym, jeśli jest zbyt długi lub złożony:

$ tar -t --exclude subdir <some.tar >to_delete.lst
$ tar --delete --no-recursion -T to_delete.lst <some.tar | ssh ...
Karel Vlk
źródło
-1

Z tego co wiem, tarpolecenie nie może używać formatu tar zarówno jako danych wejściowych, jak i wyjściowych. Będziesz musiał jakoś wyodrębnić pliki lokalnie i ponownie użyć tar, aby utworzyć plik tar w locie, z czymś takim ( -oznacza to, że zamiast pliku używane jest standardowe wejście / wyjście):

tar cf - subdir/ | ssh remote@system 'cd extractdir && tar xvf -'

Pamiętaj, tarże możliwość wyodrębnienia pliku tar bezpośrednio z innego pliku tar jest ciekawym pomysłem ...

Uriel
źródło
Bez rootowania spowoduje to utratę wszystkich informacji o własności / grupie, które wyraźnie chcę zachować.
Lekensteyn,
1
Powinieneś edytować swoje pytanie, aby uwzględnić, że nie masz dostępu do konta root na swoim hoście.
Uriel