Cách kết hợp lệnh 'tar' với 'find'


31

Lệnh find cho đầu ra này:

[root @ localhost /] # tìm 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.st Storage.log

Sau khi kết hợp với tar, nó hiển thị đầu ra này:

[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.st Storage.log

Nhưng trong khi liệt kê tệp tar, nó chỉ hiển thị một tệp duy nhất

[root @ localhost /] # tar -tvf file.tar
-rw ------- root / root 208454 2012 / 02-27 12:01 var / log / anaconda.st Storage.log

Tôi đang làm gì sai ở đây?

Với xargs tôi nhận được đầu ra này:

[root @ localhost /] # tìm var / log / -iname anaconda. * | xargs tar -cvf file1.tar

Câu hỏi thứ hai

Trong khi gõ / trước var, có nghĩa là find /var/logtại sao nó lại đưa ra mesaage tar này: Loại bỏ `/ 'hàng đầu khỏi tên thành viên

[root @ localhost /] # find / var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
tar: Loại bỏ `/ 'hàng đầu khỏi tên thành viên
/var/log/anaconda.log
tar: Loại bỏ `/ 'hàng đầu khỏi tên thành viên
/var/log/anaconda.xlog
tar: Loại bỏ `/ 'hàng đầu khỏi tên thành viên
/var/log/anaconda.yum.log
tar: Loại bỏ `/ 'hàng đầu khỏi tên thành viên
/var/log/anaconda.syslog
tar: Loại bỏ `/ 'hàng đầu khỏi tên thành viên
/var/log/anaconda.program.log
tar: Loại bỏ `/ 'hàng đầu khỏi tên thành viên
/var/log/anaconda.st Storage.log

Trong một hình thức đơn giản, sự khác biệt giữa trong hai sau đây là gì?

find var/logfind /var/log


Đây là chủ đề semi + off, nhưng tiếp tục với findlệnh, bạn nên trích dẫn cụm từ tìm kiếm. Nó hoạt động mà không đôi khi nhưng không phải luôn luôn.
nerdwaller

1
Nếu bạn sử dụng {} +thay vì {} \;nó sẽ nhóm kết quả tìm kiếm vào một đối số
Jason S

Câu trả lời:


39

Lưu ý: Xem câu trả lời của @ Iain để có giải pháp hiệu quả hơn.

Lưu ý rằng findsẽ gọi -exechành động cho mỗi tệp mà nó tìm thấy.

Nếu bạn chạy tar -cvf file.tar {}cho mỗi findđầu ra tệp duy nhất , điều này có nghĩa là bạn sẽ ghi đè lên file.tarmỗi lần, điều này giải thích lý do tại sao bạn kết thúc với một kho lưu trữ chỉ còn lại anaconda.storage.log- đó là findđầu ra tệp cuối cùng .

Bây giờ, bạn thực sự muốn nối các tệp vào kho lưu trữ thay vì tạo nó mỗi lần (đây là những gì -ctùy chọn làm). Vì vậy, sử dụng như sau:

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

Các -rtùy chọn gắn thêm vào kho lưu trữ thay vì tái tạo nó mỗi lần.

Lưu ý: Thay thế -iname anaconda.*bằng -iname "anaconda.*". Dấu hoa thị là một ký tự đại diện và có thể được mở rộng bằng vỏ của bạn trước khi findnhìn thấy nó. Để ngăn chặn sự mở rộng này, hãy bọc đối số trong dấu ngoặc kép.


Đối với tarloại bỏ hàng đầu /: Lưu trữ chỉ nên chứa tên tệp tương đối . Nếu bạn đã thêm các tệp có hàng đầu /, chúng sẽ được lưu trữ dưới dạng tên tệp tuyệt đối , nghĩa đen /var/…trên máy tính của bạn, chẳng hạn.

IIRC đây chỉ đơn giản là một biện pháp phòng ngừa cho tarcác triển khai khác ngoài GNU và cách này an toàn hơn vì bạn sẽ không ghi đè lên dữ liệu thực tế của mình /var/…khi bạn trích xuất kho lưu trữ nếu nó chứa tên tệp tương đối.


6
Nhưng lưu ý rằng nếu bạn đã cố gắng tarvào một kho lưu trữ băng thực tế theo cách này, thêm một tệp cùng lúc, tua lại băng, sau đó đọc lại toàn bộ mỗi lần để đi đến cuối, toàn bộ mọi thứ sẽ chậm một cách lố bịch. Giải pháp của bạn chỉ phù hợp nếu bạn đang ghi tệp tar vào đĩa.
Nicole Hamilton

2
Đúng, nhưng tôi nghĩ rằng chúng ta có thể bỏ qua tình huống này một cách an toàn;)
slhck

@slhck * là ký tự đại diện phải phù hợp với tất cả khả năng phải không? Nhưng ở đây find /var/log/ -iname anaconda*không cho gì và find /var/log/ -iname anaconda.*cho đầu ra, tại sao?
tối đa

Khi một ký tự đại diện được tiêu thụ, nó sẽ không còn được nhìn thấy findnữa. Vì vậy, nếu bạn có anaconda*, và trong thư mục hiện tại của bạn có một cái gì đó được đặt tên, ví dụ, anaconda5(khớp với ký tự đại diện này), ký tự đại diện sẽ được mở rộng và findsẽ thấy -iname anaconda5thay vì -iname anaconda*. Tại sao cái đầu tiên không hoạt động và cái thứ hai không phụ thuộc vào tập tin nào trong thư mục hiện tại của bạn. @max
slhck

2
Bạn có thể sử dụng {} +thay vì {} \;vậy nó sẽ nhóm các kết quả tìm kiếm vào một đối số
Jason S

41

Bạn có thể sử dụng một cái gì đó như:

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

các -print0-T làm việc cùng nhau để cho phép tên tập tin với không gian dòng mới, vv thức -nói với tar để đọc các tên tập tin đầu vào từ stdin.

Lưu ý rằng -print0phải đến cuối câu, theo câu trả lời này . Nếu không, bạn có thể sẽ nhận được nhiều tập tin hơn bạn mong đợi.


2
Bạn đã bỏ qua -nametùy chọn, khiến giải pháp của bạn cho tartoàn bộ thư mục. Nếu đó là những gì bạn muốn, bạn có thể làm điều đó dễ dàng hơn tar -cvf file.tar var/logmà không cần sử dụng find.
Nicole Hamilton

2
+1 Đường ống danh sách tarlà một ý tưởng tốt. Đó chắc chắn là giải pháp tốt nhất nếu bạn mong đợi tên đường dẫn có thể có khoảng trắng. Tôi thậm chí sẽ mô tả nó là tốt nhất về mặt kỹ thuật, vì nó đáng tin cậy và hiệu quả. Nhưng nó đòi hỏi kiến ​​thức đặc biệt bổ sung của cả hai findtar. Tôi thích thay thế lệnh khá nhiều chỉ vì nó là một công cụ tổng quát hơn: Tìm hiểu cách sử dụng nó một lần, sau đó sử dụng nó ở mọi nơi. (Nhưng tôi thừa nhận, tôi đang ở trên Windows với một cái vỏ luôn hoạt động.) Xin lỗi nếu tôi có vẻ thô lỗ.
Nicole Hamilton

2
Bạn đã có +1 của mình. Hãy hạnh phúc. :) Các dòng lệnh dài luôn là nguyên nhân của quá trình tạo i / f trên bất kỳ HĐH nào. Tôi nhớ đã cãi nhau với Mark Lucovsky tại Microsoft vào đầu những năm 90 rằng giới hạn ký tự Unicode 32K của họ quá nhỏ và khiến anh ta phàn nàn rằng tôi không biết sẽ cần thêm bao nhiêu byte để lưu trữ độ dài thay vì dài ở mọi nơi trong kernel . Thở dài. Các giải pháp trường hợp tổng quát hơn khi danh sách arg quá dài là để làm nhiều hơn trong shell (nếu có thể; trong đó là của tôi) hoặc sử dụng xargs.
Nicole Hamilton

9
nếu bạn sử dụng -print0tùy chọn find , bạn cũng cần --nulltùy chọn tar .
mivk

2
--no-unquotehóa ra cũng cần thiết: tên tệp chứa dấu gạch chéo ngược sẽ bị xử lý sai. (Không, đây không phải là giả thuyết - Tôi thực sự đang tạo một kho lưu trữ tar từ mã của người khác, chứa tên tệp có dấu gạch chéo ngược trong tên, đó là cách tôi phát hiện ra.)
hvd

12

Thử đi:

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

Bạn đã cố gắng sử dụng findđể -exec tar. Nhưng cách thức -exechoạt động của tùy chọn, nó chạy lệnh đó một lần cho mỗi tệp phù hợp mà nó tìm thấy, gây ra targhi đè lên tệp tar mà nó tạo ra mỗi lần. Đó là lý do tại sao bạn chỉ kết thúc với cái cuối cùng. Ngoài ra, bạn cần đặt dấu ngoặc kép xung quanh mẫu bạn chỉ định để findshell không mở rộng nó trước khi chuyển nó sang find.

Sử dụng thay thế lệnh bằng backticks (hoặc sử dụng $(...)ký hiệu nếu bạn thích), toàn bộ danh sách các tên được tạo ra findsẽ được dán lại vào dòng lệnh làm đối số tar, khiến nó viết tất cả chúng cùng một lúc.


2
Điều này có thể kết thúc xấu nếu tìm thấy các tệp đầu ra có khoảng trắng trong tên, dòng mới hoặc ký tự toàn cầu. Điều này chắc chắn sẽ thất bại - thiết bị xuất chuẩn từ đường ống findhiếm khi là một ý tưởng tốt. mywiki.wooledge.org/ParsingLs
slhck

3
@slhck, stdout đường ống từ find trong thực tế thường là một ý tưởng tốt, như được giải thích rất rõ ràng trong trang bạn liên kết đến trong bình luận của bạn :). Trên thực tế, đó là cách được đề xuất để làm mọi việc. Bạn chỉ nên sử dụng một số thủ thuật (chẳng hạn như read -rcủa -print0) như tôi đã làm trong câu trả lời của tôi.
terdon

4
@slhck Đây là lý do tại sao tên tệp và thư mục trong Unix và Linux theo truyền thống tránh các khoảng trắng trong tên. Đó cũng là lý do tại sao, trên Windows, nơi các tên có khoảng trắng là phổ biến, tôi đã thêm một ký hiệu thay thế lệnh bổ sung vào trình bao Hamilton C của riêng mình bằng cách sử dụng các backticks kép xử lý toàn bộ các dòng (có thể bao gồm cả khoảng trắng) như các từ đơn lẻ được dán lại vào lệnh hàng. Thật không may, không có shell Unix nào có tính năng đó.
Nicole Hamilton

1
Theo truyền thống, họ có thể tránh nó, nhưng với các tệp được tạo trong không gian người dùng thông qua GUI, bạn không thể bỏ qua các tệp có khoảng trắng nữa và coi chúng là công dân hạng hai (chỉ vì đó là Unix). Thật tuyệt khi bạn bao gồm nó trong shell của bạn, nhưng nó dành cho Windows và các shell Unix không đặc biệt cần tính năng đó nếu bạn chỉ cần sử dụng đúng cú pháp và thực hiện các biện pháp phòng ngừa thích hợp. Đó là lý do tại sao tôi đã đăng bình luận của tôi ở nơi đầu tiên.
slhck

2
Không, nhưng ở những nơi khác nó rất có thể xảy ra. Đó là lý do tại sao nên lập trình phòng thủ - tốt hơn là an toàn hơn xin lỗi. Ngoài ra, khách truy cập tìm thấy câu hỏi này có thể không nhất thiết có cùng một vấn đề và tự hỏi tại sao lệnh họ tìm thấy ở đây dường như hoạt động cho chính trường hợp này nhưng lại thất bại đối với họ. Tôi sẽ để nó cho bạn sửa lỗi, tôi chỉ nghĩ rằng việc đề cập đến nó là quan trọng vì nhiều người sớm gặp phải vấn đề này.
slhck

6

Câu hỏi 1

Lệnh của bạn thất bại vì tarlấy từng tệp được tìm thấy và lưu trữ chúng vào file.tar. Mỗi lần làm như vậy, nó sẽ ghi đè lên phần đã tạo trước đó file.tar.

Nếu những gì bạn muốn là một kho lưu trữ với tất cả các tệp, thì chỉ cần chạy tartrực tiếp, không cần find(và vâng, điều này hoạt động đối với các tệp có khoảng trắng trong tên của chúng):

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

Câu hỏi 2

Hai lệnh hoàn toàn khác nhau:

  • find var / log sẽ tìm kiếm một thư mục có tên var/log là thư mục con của thư mục hiện tại của bạn , nó tương đương với find ./var/log(chú ý ./).

  • find / var / log sẽ tìm kiếm một thư mục có tên /var/log là thư mục con của thư mục gốc/ .

/Thông điệp hàng đầu là từ tar, không phải find. Điều đó có nghĩa là nó sẽ xóa /tên tệp đầu tiên của bạn để tạo đường dẫn tuyệt đối thành tương đối . Điều này có nghĩa là tệp từ /var/log/anaconda.errorsẽ được trích xuất ./var/log/anaconda.errorkhi bạn hủy lưu trữ.


1

Có hai cách -execcó thể làm việc. Một cách chạy lệnh nhiều lần - một lần cho mỗi tệp; cách khác chạy lệnh một lần, bao gồm tất cả các tệp dưới dạng danh sách các tham số.

  • -exec tar -cvf file.tar {} ';'chạy tarlệnh cho mỗi tệp, ghi đè lưu trữ mỗi lần.
  • -exec tar -cvf file.tar {} '+'chạy tarlệnh một lần, tạo một kho lưu trữ của tất cả các tệp được tìm thấy.

1

Tôi nghĩ rằng việc sử dụng -exec cho mỗi tệp có thể làm cho quá trình nén tar rất chậm, nếu bạn có nhiều tệp. Tôi thích sử dụng lệnh:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar

cho đến khi nó bắt đầu thất bại với/bin/cpio: xxx: Cannot open: Too many open files
SYN
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.