Tại sao `zip` trong một vòng lặp for hoạt động khi tệp tồn tại, nhưng không phải khi nó không hoạt động?


7

Tôi có một thư mục chứa một số thư mục con. Có một câu hỏi về việc nén các tập tin có chứa một câu trả lời mà tôi đã từng sửa đổi đôi chút cho nhu cầu của mình.

for i in */; do zip "zips/${i%/}.zip" "$i*.csv"; done

Tuy nhiên, tôi gặp phải một vấn đề kỳ quái. Đối với bộ thư mục đầu tiên, nơi zips/<name>.zipkhông tồn tại, tôi gặp lỗi này:

zip error: Nothing to do! (zips/2014-10.zip)
        zip warning: name not matched: 2014-11/*.csv

tuy nhiên khi tôi chỉ echobáo cáo zip:

for i in */; do echo zip "zips/${i%/}.zip" "$i*.csv"; done

Sau đó chạy lệnh echo ( zip zips/2014-10.zip 2014-10/*.csv), nó hoạt động tốt và nén thư mục. Sau đó, điều thú vị ở đây là các lần chạy tiếp theo của lệnh gốc sẽ thực sự nén các thư mục không hoạt động lần đầu tiên!

Để tự kiểm tra hành vi này:

cd /tmp
mkdir -p 2016-01 2016-02 2016-03 zips
for i in 2*/; do touch "$i"/one.csv; done
for i in 2*/; do touch "$i"/two.csv; done
zip zips/2016-03.zip 2016-03/*.csv
for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done
for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done

Bạn sẽ thấy rằng tiếng vang in ra những tuyên bố này:

zip zips/2016-01.zip 2016-01/*.csv
zip zips/2016-02.zip 2016-02/*.csv
zip zips/2016-03.zip 2016-03/*.csv

Tuy nhiên, lệnh zip thực tế sẽ cho bạn biết:

        zip warning: name not matched: 2016-01/*.csv

zip error: Nothing to do! (zips/2016-01.zip)
        zip warning: name not matched: 2016-02/*.csv

zip error: Nothing to do! (zips/2016-02.zip)
updating: 2016-03/one.csv (stored 0%)
updating: 2016-03/two.csv (stored 0%)

Vì vậy, nó thực sự cập nhật tệp zip với .csvs nơi tệp zip tồn tại, nhưng không phải khi tệp zip được tạo. Và nếu bạn sao chép một trong các lệnh zip:

$ zip zips/2016-02.zip 2016-02/*.csv
adding: 2016-02/one.csv (stored 0%)
adding: 2016-02/two.csv (stored 0%)

Sau đó chạy lại zip-all-the-thing:

for i in 2*/; do zip "zips/${i%/}.zip" "$i*.csv"; done

Bạn sẽ thấy rằng nó cập nhật cho 2016-022016-03. Đây là đầu ra của tôi về tree:

.
├── 2016-01
   ├── one.csv
   └── two.csv
├── 2016-02
   ├── one.csv
   └── two.csv
├── 2016-03
   ├── one.csv
   └── two.csv
└── zips
    ├── 2016-02.zip
    └── 2016-03.zip

Ngoài ra, (un) đáng ngạc nhiên, điều này hoạt động tốt:

zsh -c "$(for i in 2*/; do echo zip "zips/${i%/}.zip" "$i*.csv"; done)"

Tôi làm gì sai ở đây? (lưu ý, tôi đang sử dụng zsh thay vì bash, nếu điều đó tạo ra sự khác biệt)


Bạn có thể hiển thị "lệnh echo" chính xác mà nó xuất ra và bạn đã chạy thành công?
JigglyNaga

@JigglyNaga zip zips/2014-10.zip 2014-10/*.csv(cũng được thêm vào Q)
Wayne Werner

1
Lỗi là về không có *cvstập tin hiện có, không thiếu .zip. Bạn có thể cho chúng tôi một ví dụ tối thiểu về cấu trúc thư mục / tệp bạn đang sử dụng để chúng tôi có thể tạo lại nó và kiểm tra lỗi không?
terdon

@terdon xong - Các bước để repro, cùng với đầu ra của cây. Có vẻ như một hành vi rất lạ đối với tôi. Nếu nó chỉ đơn giản là vấn đề bằng cách nào đó thì lệnh nằm trong thư mục sai, tôi hy vọng rằng nó thất bại đối với tất cả các tệp zip, nhưng nó hoạt động với những tệp được tạo thủ công ...
Wayne Werner

Đã thêm một cách giải quyết ... nhưng tại sao tác phẩm gốc không?
Wayne Werner

Câu trả lời:


14

Mở rộng bằng vỏ

Các trích dẫn xung quanh "$i*.csv"làm cho sự khác biệt. Với dấu ngoặc kép, shell mở rộng chuỗi đó thành "2014-11 / *. Csv". Tập tin chính xác đó không tồn tại và zipbáo cáo lỗi. Không có dấu ngoặc kép, *cũng mở rộng (thông qua mở rộng tên tệp / "globalbing") và ziplệnh kết quả là một danh sách đầy đủ các tệp phù hợp, mỗi tệp là một đối số riêng biệt. Bạn có thể có hành vi thứ hai, bên trong forvòng lặp, với:

for i in */ ; do zip "zips/${i%/}.zip" "$i"*.csv ; done

Mở rộng bằng zip

zipcũng có thể mở rộng các ký tự đại diện cho chính nó, nhưng không phải trong mọi tình huống. Từ hướng dẫn sử dụng zip :

Các zip chương trình có thể thực hiện phù hợp với nhau trên những cái tên được trong zip archive được sửa đổi hoặc, trong trường hợp của -x (loại trừ) hoặc -i (bao gồm) tùy chọn, trong danh sách các tập tin cần được phẫu thuật, bằng cách sử dụng dấu gạch chéo ngược hoặc dấu ngoặc kép để báo cho shell không thực hiện mở rộng tên.

Lệnh ban đầu hoạt động cho các lần thử tiếp theo, sau khi bạn đã tạo thành công một kho lưu trữ, vì zipcố gắng khớp các ký tự đại diện với nội dung của kho lưu trữ hiện có. Chúng tồn tại ở đó và vẫn tồn tại trên hệ thống tập tin, vì vậy chúng được báo cáo updating:.

Để zipxử lý các ký tự đại diện khi tạo tệp lưu trữ, hãy sử dụng -rtùy chọn (recurse) để lặp lại vào thư mục được yêu cầu và -i(bao gồm) để giới hạn nó trong các tệp khớp với mẫu:

 for i in */ ; do zip -r "zips/${i%/}.zip" "$i" -i '*.csv' ; done
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.