Jak połączyć polecenie „tar” z „find”

31

Polecenie find daje następujące dane wyjściowe:

[root @ localhost /] # find var / log / -iname anaconda. *
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Po połączeniu ze smołą wyświetla następujące dane wyjściowe:

[root @ localhost /] # find var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Ale podczas wyświetlania pliku tar wyświetla tylko jeden plik

[root @ localhost /] # tar -tvf plik.tar
-rw ------- root / root 208454 27.02.2012 12:01 var / log / anaconda.storage.log

Co tutaj robię źle?

Z xargs otrzymuję ten wynik:

[root @ localhost /] # find var / log / -iname anaconda. * | xargs tar -cvf plik1.tar

Drugie Pytanie

Podczas pisania / przed var, oznacza to, find /var/logdlaczego daje on ten komunikat tar: Usuwanie wiodącego `/ 'z nazw członków

[root @ localhost /] # find / var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
tar: Usuwanie wiodącego `/ 'z nazw członków
/var/log/anaconda.log
tar: Usuwanie wiodącego `/ 'z nazw członków
/var/log/anaconda.xlog
tar: Usuwanie wiodącego `/ 'z nazw członków
/var/log/anaconda.yum.log
tar: Usuwanie wiodącego `/ 'z nazw członków
/var/log/anaconda.syslog
tar: Usuwanie wiodącego `/ 'z nazw członków
/var/log/anaconda.program.log
tar: Usuwanie wiodącego `/ 'z nazw członków
/var/log/anaconda.storage.log

W prostej formie jaka jest różnica między następującymi dwoma?

find var/log i find /var/log

max
źródło
To jest pół + off tematu, ale idąc dalej z findpoleceniem, powinieneś zacytować szukany termin. Działa czasami, ale nie zawsze.
nerdwaller
1
Jeśli użyjesz {} +zamiast {} \;tego zgrupuje wyniki find w jeden argument
Jason S

Odpowiedzi:

39

Uwaga: Zobacz odpowiedź @ Iaina na bardziej wydajne rozwiązanie.

Pamiętaj, że findwywoła -execakcję dla każdego znalezionego pliku .

Jeśli uruchamiasz tar -cvf file.tar {}dla każdego pojedynczego pliku findwyjściowego, oznacza to, że za file.tarkażdym razem nadpisujesz , co wyjaśnia, dlaczego kończysz się pozostawieniem jednego archiwum, które zawiera tylko anaconda.storage.log- są to ostatnie pliki findwyjściowe.

Teraz faktycznie chcesz dodawać pliki do archiwum zamiast tworzyć go za każdym razem (tak robi ta -copcja). Więc skorzystaj z następujących opcji:

find var/log/ -iname "anaconda.*" -exec tar -rvf file.tar {} \;

-rOpcja dołącza do archiwum zamiast odtwarzając go za każdym razem.

Uwaga: Wymień -iname anaconda.*się -iname "anaconda.*". Gwiazdka jest symbolem wieloznacznym i może być rozszerzona przez powłokę, zanim findją zobaczy. Aby temu zapobiec, zawiń argument w podwójne cudzysłowy.


Jeśli chodzi o tarusuwanie wiodących /: archiwum powinno zawierać tylko względne nazwy plików. Jeśli dodasz pliki ze znakiem wiodącym /, zostaną one zapisane jako bezwzględne nazwy plików, dosłownie /var/…na przykład na twoim komputerze.

IIRC jest to po prostu środek ostrożności dla tarimplementacji innych niż GNU, i jest bezpieczniejszy w ten sposób, ponieważ nie zastąpisz faktycznych danych /var/…podczas rozpakowywania archiwum, jeśli zawiera względne nazwy plików.

slhck
źródło
6
Pamiętaj jednak, że jeśli spróbujesz w tarten sposób archiwizować taśmę, dodając jeden plik naraz, przewijając taśmę, a następnie ponownie czytając wszystko za każdym razem, aby dojść do końca, wszystko będzie absurdalnie wolne. Twoje rozwiązanie jest odpowiednie tylko wtedy, gdy zapisujesz plik tar na dysk.
Nicole Hamilton,
2
To prawda, ale myślę, że możemy spokojnie zignorować tę sytuację;)
slhck
@slhck * to symbol wieloznaczny, który powinien pasować do wszystkich możliwości, prawda? ale tutaj find /var/log/ -iname anaconda*nic nie daje i find /var/log/ -iname anaconda.*daje wynik, dlaczego?
maks.
Gdy zostanie zużyty symbol wieloznaczny, już go nie będzie widać find. Więc jeśli tak anaconda*, a w bieżącym folderze jest coś o nazwie na przykład anaconda5(pasujące do tego symbolu wieloznacznego), symbol wieloznaczny zostanie rozwinięty i zamiast niego findzobaczysz . Dlaczego pierwszy nie działa, a drugi działa, zależy od tego, jakie pliki znajdują się w bieżącym katalogu. @max-iname anaconda5-iname anaconda*
slhck
2
Możesz użyć {} +zamiast, {} \;aby zgrupować wyniki find w jednym argumencie
Jason S
41

Możesz użyć czegoś takiego jak:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -

-print0I -Twspółpracują ze sobą w celu umożliwienia nazwy plików ze spacjami nowej linii itp Ostateczna -nakazuje tar odczytać nazwy plików wejściowych z stdin.

Pamiętaj, że -print0zgodnie z tą odpowiedzią musi znajdować się na końcu Twojego oświadczenia . W przeciwnym razie prawdopodobnie otrzymasz więcej plików, niż się spodziewasz.

Peter Mortensen
źródło
2
Pominąłeś tę -nameopcję, powodując rozwiązanie dla tarcałego katalogu. Jeśli tego właśnie chcesz, możesz to zrobić łatwiej niż tar -cvf file.tar var/logbez użycia find.
Nicole Hamilton,
2
+1 Pipingowanie listy tarto dobry pomysł. Jest to zdecydowanie najlepsze rozwiązanie, jeśli spodziewasz się, że nazwy ścieżek mogą zawierać spacje. Opisałbym to nawet jako najlepsze technicznie, ponieważ jest zarówno niezawodne, jak i wydajne. Ale wymaga to dodatkowej specjalnej wiedzy na temat obu findi tar. Wolę podstawianie poleceń tylko dlatego, że jest to bardziej ogólne narzędzie: Naucz się go używać raz, a następnie używaj go wszędzie. (Ale przyznaję, jestem w systemie Windows z powłoką, w której zawsze działa.) Przepraszam, jeśli wydawałem się niegrzeczny.
Nicole Hamilton,
2
Masz już +1. Bądź szczęśliwy. :) Długie linie poleceń są zawsze zmorą procesu tworzenia i / f w dowolnym systemie operacyjnym. Pamiętam, jak na początku lat 90. kłóciłem się z Markiem Lucovskim w Microsofcie, że ich limit 32K znaków Unicode w NT jest zbyt mały i po jego narzekaniu nie miałem pojęcia, ile więcej bajtów zajmie przechowywanie długości jako długości, a nie skrótów w całym jądrze . Westchnienie. Bardziej ogólne rozwiązania przypadków, gdy lista argumentów jest zbyt długa, to zrobić więcej w powłoce (jeśli to możliwe; w mojej jest) lub użyć xargs.
Nicole Hamilton,
9
jeśli użyjesz -print0opcji find , potrzebujesz również --nullopcji tar .
mivk
2
I --no-unquoteokazuje się być również potrzebny: nazwy plików zawierające ukośniki odwrotne byłyby w przeciwnym razie źle traktowane. (Nie, to nie jest hipotetyczne - naprawdę tworzę archiwum tar z kodu innej osoby, zawierające nazwę pliku z odwrotnymi ukośnikami w nazwie, tak się dowiedziałem.)
hvd
12

Spróbuj tego:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`

Próbowałeś użyć finddo -exec tar. Ale sposób działania tej -execopcji powoduje, że uruchamia to polecenie raz dla każdego znalezionego pasującego pliku, co powoduje tarzastąpienie tworzonego pliku tar za każdym razem. Właśnie dlatego skończyłeś z ostatnim. Ponadto musisz umieścić cudzysłowy wokół określonego wzorca, aby findpowłoka go nie rozwijała przed przekazaniem go find.

Używając podstawiania komend za pomocą odwrotnych $(...)znaków (lub używając notacji, jeśli wolisz), cała lista utworzonych nazw findjest wklejana z powrotem do wiersza komend jako argumenty tar, powodując, że zapisują je wszystkie naraz.

Nicole Hamilton
źródło
2
Może to skończyć się źle, jeśli znajdziesz pliki wyjściowe ze spacjami w nazwie, znakami nowej linii lub znakami globowania. To na pewno się nie powiedzie - odpalanie stdout z findrzadko jest dobrym pomysłem. mywiki.wooledge.org/ParsingLs
slhck
3
@slhck, pipowanie standardowego wyjścia z find jest w rzeczywistości dobrym pomysłem, jak to bardzo jasno wyjaśniono na stronie, do której prowadziłeś link w swoim komentarzu :). Jest to w rzeczywistości zalecany sposób robienia rzeczy. Należy po prostu korzystać z niektórych trików (takie jak read -rz -print0) jak ja w mojej odpowiedzi.
terdon
4
@slhck Dlatego nazwy plików i katalogów w systemach Unix i Linux tradycyjnie unikały spacji w nazwach. Z tego też powodu w systemie Windows, w którym nazwy ze spacjami są wspólne, dodałem dodatkową notację zastępującą polecenie do mojej własnej powłoki Hamilton C, używając podwójnych odwrotnych znaków , traktując całe linie (ewentualnie łącznie ze spacjami) jako pojedyncze słowa, które należy wkleić z powrotem do polecenia linia. Niestety, żadna z powłok uniksowych nie ma tej funkcji.
Nicole Hamilton,
1
Tradycyjnie mogliby tego uniknąć, ale ponieważ pliki tworzone są w przestrzeni użytkownika za pomocą GUI, nie można już zaniedbywać plików ze spacjami i traktować ich jak obywateli drugiej kategorii (tylko dlatego, że jest to Unix). Fajnie, że umieściłeś to w swojej powłoce, ale dotyczy to systemu Windows, a powłoki Unix nie potrzebują tej funkcji, jeśli po prostu użyjesz odpowiedniej składni i podejmiesz odpowiednie środki ostrożności. Dlatego w pierwszej kolejności opublikowałem swój komentarz.
slhck
2
Nie, ale w innych miejscach może się to zdarzyć. Dlatego warto programować defensywnie - lepiej być bezpiecznym niż żałować. Również odwiedzający, którzy znajdą to pytanie, niekoniecznie mają ten sam problem i zastanawiają się, dlaczego polecenie, które tu znaleźli, wydawało się działać w tym przypadku, ale nie udało się im. Pozwolę ci naprawić polecenie, pomyślałem tylko, że ważne jest, aby o tym wspomnieć, ponieważ wiele osób prędzej czy później napotyka ten problem.
slhck
6

Pytanie 1

Twoje polecenie kończy się niepowodzeniem, ponieważ tarpobiera każdy ze znalezionych plików i archiwizuje je file.tar. Za każdym razem spowoduje to zastąpienie wcześniej utworzonego file.tar.

Jeśli chcesz mieć jedno archiwum ze wszystkimi plikami, po prostu uruchom je tarbezpośrednio, nie ma potrzeby find(i tak, działa to w przypadku plików ze spacjami w nazwach):

tar -vcf file.tar /var/log/anaconda*   

pytanie 2

Te dwa polecenia są zupełnie różne:

  • find var / log przeszuka katalog o nazwie, var/log który jest podkatalogiem twojego bieżącego katalogu , jest to równoważne find ./var/log(zauważ ./).

  • znaleźć / var / log wyszuka katalog o nazwie /var/log , która jest podkatalogu root/ .

Wiadomość wiodąca /pochodzi od tarnie find. Oznacza to, że usuwa pierwszą /z twoich nazw plików, aby przekształcić ścieżki bezwzględne w względne . Oznacza to, że plik /var/log/anaconda.errorzostanie wyodrębniony do ./var/log/anaconda.errormomentu rozpakowania archiwum.

terdon
źródło
1

Istnieją dwa sposoby -execdziałania. Jeden sposób uruchamia polecenie wiele razy - raz dla każdego pliku; w inny sposób uruchamia polecenie raz, włączając wszystkie pliki jako listę parametrów.

  • -exec tar -cvf file.tar {} ';'uruchamia tarpolecenie dla każdego pliku, zastępując archiwum za każdym razem.
  • -exec tar -cvf file.tar {} '+'uruchamia tarpolecenie raz, tworząc archiwum wszystkich znalezionych plików.
mwfearnley
źródło
1

Myślę, że użycie -exec dla każdego pliku może spowolnić kompresję tar, jeśli masz dużo plików. Wolę użyć polecenia:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar
fabceolin
źródło
dopóki nie zacznie działać z/bin/cpio: xxx: Cannot open: Too many open files
SYN