Unix: làm thế nào để tar chỉ N tập tin đầu tiên của mỗi thư mục?


7

Tôi có một thư mục chứa 2Gb hình ảnh, với các thư mục con sâu vài cấp.

Tôi chỉ muốn lưu trữ Ncác tệp của mỗi thư mục (phụ) trong một tệp tar. Tôi đã cố gắng sử dụng findsau tailđó tarnhưng không thể quản lý để làm cho nó hoạt động. Đây là những gì tôi đã thử (giả sử N = 10):

find . | tail -n 10 | tar -czvf backup.tar.gz

Có thể đưa ra lỗi này:

Cannot stat: File name too long

Có chuyện gì ở đây vậy? nghĩ về nó - ngay cả khi nó hoạt động tôi nghĩ nó sẽ chỉ tar 10 tệp đầu tiên của tất cả các thư mục, không phải 10 tệp của mỗi thư mục.

Làm thế nào tôi có thể nhận được Ncác tập tin của từng thư mục? (Không cần đặt hàng tập tin)


+0. tại sao bạn muốn làm việc này?
không thể

Bạn có muốn có 10 tệp đầu tiên, được sắp xếp theo số, bảng chữ cái, theo ngày hoặc thứ tự không quan trọng?
Alexander

@unfitableid: Tôi phải làm việc trên một trang web tại địa phương, nhưng có 9Gb hình ảnh trên máy chủ và tôi không cần tất cả chúng!
Sam

@Sam: À. Nâng cao.
không thể

Câu trả lời:


4

Nếu bạn paxhỗ trợ -0tùy chọn, với zsh:

print -rN dir/**/*(D/e:'reply=($REPLY/*(ND^/[1,10]))':) |
  pax -w0 | xz > file.tar.xz

Nó bao gồm 10 tệp không phải thư mục đầu tiên của mỗi thư mục trong danh sách được sắp xếp theo tên tệp. Bạn có thể chọn một thứ tự sắp xếp khác bằng cách thêm omvòng loại toàn cầu (thứ tự theo thời gian sửa đổi, Omđể đảo ngược thứ tự), oL(thứ tự theo độ dài), non(sắp xếp theo tên nhưng bằng số) ...

Nếu bạn không có paxlệnh tiêu chuẩn hoặc không hỗ trợ -0nhưng bạn có tarlệnh GNU , bạn có thể thực hiện:

print -rN -- dir/**/*(D/e:'reply=($REPLY/*(ND^/[1,10]))':) |
  tar --null -T - -cjf file.tar.xz

Nếu bạn không thể sử dụng zsh, nhưng có quyền truy cập bash(vỏ của dự án GNU), bạn có thể làm:

find dir -type d -exec bash -O nullglob -O dotglob -c '
  for dir do
    set -- "$dir/*"; n=0
    for file do
      if [ ! -d "$file" ] || [ -L "$file" ]; then
        printf "%s\0" "$file"
        (( n++ < 10 )) || break
      fi
    done
  done' bash {} + | pax -0w | xz > file.tar.xz

Điều đó sẽ ít hiệu quả hơn đáng kể mặc dù.


print -rN - dir / ** / * (D / e: 'reply = ($ REPLY / * (ND ^ / [1,10]))' :) -> Nhận thông báo lỗi: -bash: lỗi cú pháp gần mã thông báo bất ngờ `('
Sam

1
@ user1916171, Có vẻ như bạn đã bỏ lỡ với zshNếu bạn không thể sử dụng các phần zsh của câu trả lời đó. bashlà một shell khác, shell của dự án GNU bị giới hạn hơn nhiều so với zsh. Xem phần cuối của câu trả lời cho một giải pháp sẽ làm việc với bash.
Stéphane Chazelas

2

Giả sử thư mục chính của bạn là /tmp/dirtừ đó bạn muốn lưu trữ chỉ các tệp N (ví dụ N = 10) của mỗi thư mục (phụ) bên dưới vào một backup.tar.gztệp.

Ví dụ treecho /tmp/dir:

dir/                                                                                                                                                                                                           
├── one
│   ├── one10.txt
│   ├── one11.txt
│   ├── one1.txt
│   ├── one2.txt
│   ├── one3.txt
│   ├── one4.txt
│   ├── one5.txt
│   ├── one6.txt
│   ├── one7.txt
│   ├── one8.txt
│   ├── one9.txt
│   └── one_deep
│       ├── one_deep1
│       ├── one_deep10
│       ├── one_deep11
│       ├── one_deep2
│       ├── one_deep3
│       ├── one_deep4
│       ├── one_deep5
│       ├── one_deep6
│       ├── one_deep7
│       ├── one_deep8
│       └── one_deep9
├── three
│   ├── three10.txt
│   ├── three11.txt
│   ├── three1.txt
│   ├── three2.txt
│   ├── three3.txt
│   ├── three4.txt
│   ├── three5.txt
│   ├── three6.txt
│   ├── three7.txt
│   ├── three8.txt
│   ├── three9.txt
│   └── three_deep
│       ├── three_deep1
│       ├── three_deep10
│       ├── three_deep11
│       ├── three_deep2
│       ├── three_deep3
│       ├── three_deep4
│       ├── three_deep5
│       ├── three_deep6
│       ├── three_deep7
│       ├── three_deep8
│       └── three_deep9

Mã số:

cd /tmp; for i in `find dir/* -type d`; do find $i -maxdepth 1 -type f | tail -n 10 | xargs -I file tar -rf backup.tar file; done; gzip backup.tar

Điều này sẽ tạo ra backup.tar.gzvới 10 tệp của mỗi thư mục con từ bên dưới /tmp/dir.


Nếu vì lý do nào đó cd /tmpkhông thành công, bạn sẽ chạy lệnh đó trong thư mục sai. Bạn phải luôn kiểm tra trạng thái thoát của cd:cd /tmp && for...
Stéphane Chazelas

find dir/* -type dcó nghĩa là bạn sẽ không xử lý các thư mục ẩn trong thư mục hiện tại, nhưng sẽ xử lý chúng trong các thư mục con. Sử dụng find dir -type dhoặc nếu bạn không muốn các tệp trong thư mục hiện tại: find dir/. ! -name . -type dhoặc find dir ! -path dir -type d.
Stéphane Chazelas

Sử dụng `find...`có nghĩa là gọi toán tử split + global (chỉ tách trong zsh). Ở đây bạn không muốn phần glob, và bạn muốn chia trên xuống dòng chỉ (mặc dù xuống dòng là một nhân vật có giá trị trong một tên file, vì vậy cách tiếp cận đó là thiếu sót dù sao, và bạn nên sử dụng find-execthay vì).
Stéphane Chazelas

Rời khỏi điều đó $icũng không có nghĩa là gọi toán tử chia + toàn cầu không có ý nghĩa ở đây. Sử dụngfind "$i"
Stéphane Chazelas

Lưu ý rằng ngay cả với -I, xargsvẫn xử lý các ký tự trích dẫn và dấu gạch chéo ngược đặc biệt trong đầu vào của nó. Nó cũng có nghĩa là chạy một tarlệnh trên mỗi dòng không hiệu quả lắm.
Stéphane Chazelas

2

Vì đầu ra findlà phẳng, bạn không thực sự biết tệp nào thuộc cùng thư mục mà không cần nhìn vào đường dẫn. Cách khác là sử dụng nhiều finds (một cho mỗi thư mục) mà không cần phải nhìn vào đường dẫn. Đây là những gì tôi đã làm. Để tar tối đa 10 tệp của mỗi thư mục con, hãy sử dụng một cái gì đó như thế này:

for dir in $(find . -type d); do
  find "$dir" -maxdepth 1 -type f -printf "\"%p\"\n" | tail -10
done | xargs tar cvfz backup.tar.gz

Điều này đệ quy tìm thấy tất cả các thư mục trong thư mục hiện tại. Đối với mỗi thư mục, nó tìm thấy tối đa 10 tệp trong chính thư mục đó ( -maxdepth 1). Khi toàn bộ vòng lặp kết thúc, tarlệnh được thực thi trên tất cả các tệp được đầu ra bởi vòng lặp. Tôi cũng đã tính đến tên thư mục và thư mục có khoảng trắng bằng cách trích dẫn $dirfindin từng tên tệp trong dấu ngoặc kép bằng -printftùy chọn.


1
for d in ./*/
do
    cd "$d"
    tar -rvf ../backup.tar $(ls | tail -10)
    cd ..
done
gzip backup.tar

biến thể khác

find * -prune -type d -exec bash -c 'printf "%s\n" $0/* | tail -10' {} \; |
tar czvf backup.tar.gz -T -

0

Sử dụng hàm băm trên tên thư mục và chỉ phát ra tên tệp nếu số giá trị băm nằm dưới ngưỡng. Ví dụ

find . -depth -type f \
| perl -MFile::Spec -nle '(undef,$d,$f)=File::Spec->splitpath($_); print if $seen{$d}++ < 3' \
| tar ...

0

Cách dễ nhất (hoặc dễ hiểu nhất) là sử dụng xargs với -N max-argstùy chọn.

Hãy nhớ rằng, đầu vào của bạn luôn cần phải là một cái gì đó, không yêu cầu một dòng lệnh, vì vậy echo *.*sẽ hoạt động như đầu vào, trong ls *.*đó không (dòng lệnh ls quá dài)

Tìm nên ổn, vì đối số của nó chỉ là đường dẫn, không phải là danh sách các tệp.


0

OP cũng đã hỏi điều này trong Stackoverflow . Đây là câu trả lời tôi đưa ra ở đó.

Việc lựa chọn và thứ tự các tệp trong câu trả lời này được xác định theo thứ tự ngoài find, vì vậy "đầu tiên" không được xác định rõ ở đây. Điều này cũng có thể phụ thuộc vào GNU Awk 4.1.0.

tìm thấy . -type f |
awk -v N = 10 -F / 'khớp ($ 0, /.*\//, m) && a [m [0]] ++ <N' |
xargs -r -d '\ n' tar -rvf /tmp/backup.tar

gzip /tmp/backup.tar

Bình luận:

  1. sử dụng find . -type fđể đảm bảo rằng các tệp có tiền tố tên thư mục hàng đầu, vì vậy bước tiếp theo có thể hoạt động
  2. các awklệnh theo dõi những tên tuổi hàng đầu thư mục, và phát ra tên đường dẫn đầy đủ cho đến khi N (10, ở đây) file với thư mục hàng đầu cùng đã được phát ra (có lẽ đơn giản hơn awksử dụng - tách thông tin mô hình và chương trình - có thể là khả năng di chuyển)
  3. sử dụng xargsđể gọi tar- chúng tôi đang thu thập tên tệp thông thường và chúng cần phải là đối số cho lệnh lưu trữ đó
  4. xargscó thể gọi tarnhiều lần, vì vậy chúng tôi sẽ thêm (tùy chọn -r) vào một kho lưu trữ đơn giản, sau đó nén nó sau khi tất cả được viết

Ngoài ra, bạn có thể không muốn ghi một tập tin sao lưu vào thư mục hiện tại, vì bạn đang quét nó - đó là lý do tại sao đề xuất này ghi vào / tmp.

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.