tìm | xargs shasum tạo ra tổng kiểm tra của tập tin tổng kiểm tra (sớm) và thất bại khi kiểm tra


10

Vấn đề của tôi (trong một tập lệnh với #!/bin/sh) là như sau: Tôi cố kiểm tra tất cả các tệp trong một thư mục cho mục đích lưu trữ. Tệp tổng kiểm tra (trong trường hợp của tôi là sha1) với tất cả tên tệp sẽ nằm trong cùng một thư mục. Hãy nói rằng chúng tôi có một thư mục ~/testvới các tập tin f1f2:.

mkdir ~/test
cd ~/test
echo "hello" > f1
echo "world" > f2

Bây giờ tính toán tổng kiểm với

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum

thực hiện chính xác những gì tôi muốn, nó chỉ liệt kê tất cả các tệp của thư mục hiện tại và tính toán tổng sha1 (maxdepth có thể được thay đổi sau). Đầu ra trên STDOUT là:

f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2

Thật không may, khi cố lưu nó vào một tệp với

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum > sums.sha1

tệp kết quả sẽ hiển thị tổng kiểm tra cho chính nó:

da39a3ee5e6b4b0d3255bfef95601890afd80709  sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2  

và do đó thất bại sau đó shasum --check, vì vấn đề rõ ràng về sửa đổi tệp bổ sung khi lưu số tiền cuối cùng.

Tôi nhìn xung quanh và bằng cách sử dụng -pcờ cho xargs, tôi phát hiện ra rằng bằng cách nào đó nó tạo ra tệp đầu ra trước khi thực hiện lệnh find, do đó tệp bổ sung được tìm thấy và sẽ được kiểm tra lại ...

Tôi biết rằng như một cách giải quyết, tôi có thể lưu tổng kiểm tra vào một vị trí khác (thư mục tạm thời thông qua mktemp) hoặc loại trừ nó một cách cụ thể, nhưng tôi muốn hiểu tại sao nó hoạt động theo cách của nó - điều này trong mắt tôi không hữu ích, ví dụ: nếu lệnh đầu tiên sẽ kiểm tra xem tệp đầu ra đã có trên đĩa chưa, nó sẽ không bao giờ có câu trả lời đúng ...


8
Không phải xargs, chính shell tạo ra tệp này, bởi vì trước khi bất kỳ lệnh nào được thực thi, trước tiên shell sẽ chuyển hướng tất cả đầu vào, đầu ra và đường ống, để khi findbắt đầu tệp đầu ra đã tồn tại. Sử dụng -execthay thế:find -maxdepth 1 -type f -exec sh -c 'shasum "$@" > sums.sha1' {} +
jimmij

@jimmij, điều đó không được đảm bảo để hoạt động nếu một số shyêu cầu là cần thiết. Lưu ý rằng bạn cần một đối số cho $0trước {}.
Stéphane Chazelas

@jimmij Câu trả lời khác của bạn mà đề nghị teeđã biến mất? Tôi đã thử nó và nó hoạt động tốt, tôi cũng đã loại bỏ STDOUT bằng cách thêm 1>/dev/null. Có điều gì đó sai với câu trả lời hoặc đó là một lỗi?
121391

@ user121391 Stephane chỉ ra rằng đôi khi có thể có vấn đề về tình trạng chủng tộc, điều có vẻ đúng. Tôi đã xóa nó trong một thời gian để bạn có thể xem, nhưng nếu bạn có nhiều tệp trong danh sách thì lệnh đó có thể sai.
jimmij

@jimmij ah, tôi hiểu rồi. Nó có thể hữu ích nếu bạn đặt trước nó với một cảnh báo về các vấn đề, bởi vì tôi nghĩ nó không được biết đến nhiều đến mức điều này có thể xảy ra. Mặt khác, tôi đã chấp nhận câu trả lời của bạn cho các trường hợp nếu các lần chạy định kỳ bao gồm tệp cũ và Anthon cho các trường hợp cần ghi đè.
121391

Câu trả lời:


12

Bạn có thể ngăn không cho tập tin tiếp cận xargsbằng cách sử dụng:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\n' |
  xargs -r shasum -- > sums.sha1

Để ngăn chặn sự cố với tên tệp có khoảng trống hoặc dòng mới hoặc dấu ngoặc kép hoặc dấu gạch chéo ngược, tuy nhiên tôi sẽ sử dụng:

find . -maxdepth 1 -type f ! -name sums.sha1 -printf '%P\0' |
  xargs -r0 shasum -- > sums.sha1

thay thế.

Các --là để tránh các vấn đề với tên tập tin bắt đầu bằng -. Tuy nhiên nó sẽ không giúp cho một tập tin được gọi là -. Nếu bạn đã sử dụng -print0thay vì -printf '%P\0', bạn sẽ không cần --và sẽ không gặp vấn đề với -tập tin.


Giải pháp của bạn là những gì tôi đã sử dụng. Tôi đặc biệt thích những lần chạy tiếp theo không làm lại tập tin tổng kiểm tra và làm phồng thư mục. Ngoài ra, trong kịch bản của tôi, tôi đã sử dụng basenameđể lấy tên tệp sums.sha1 từ đường dẫn đầy đủ nhất định (điều này không được bao gồm trong câu hỏi, nhưng nó có thể giúp người khác).
121391

7

Vì bạn đang sử dụng -maxdepth 1, tôi cho rằng bạn không muốn đệ quy. Nếu vậy, chỉ cần làm nó trong vỏ thay thế:

for f in ~/test/*; do
    shasum -- "$f"
done > sums.sha1

Để bỏ qua các thư mục, bạn có thể làm:

for f in ~/test/*; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

Nếu bạn cần đệ quy và đang sử dụng bash, hãy làm:

shopt -s globstar
for f in ~/test/**; do
    [ ! -d "$f" ] && shasum -- "$f"
done > sums.sha1

Lưu ý rằng tất cả các cách tiếp cận này có lợi ích khi làm việc trên các tên tệp tùy ý, bao gồm cả các tên có dấu cách, dòng mới hoặc bất cứ thứ gì khác.


Tôi nghĩ bạn sẽ đề cập rằng điều này sẽ giải quyết mọi vấn đề mà OP sẽ gặp phải với tên tệp có dòng mới trong đó. Mặt khác, nếu sums.sha1đã có sẵn (từ lần chạy trước), giải pháp của bạn sẽ kết hợp nó.
Anthon

Xin lỗi, trước đây tôi không làm rõ: maxdepth chỉ được sử dụng trong ví dụ này, tôi sử dụng chức năng mà người dùng / tập lệnh có thể cung cấp bất kỳ giá trị nào, mặc dù hiện tại tôi chỉ cần độ sâu 1.
user121391

@ user121391 xem câu trả lời cập nhật cho cách tiếp cận đệ quy.
terdon

Lưu ý rằng nó cũng sẽ cố kiểm tra các loại tệp không thường xuyên khác như đường ống, thiết bị ... (và liên kết tượng trưng cho chúng).
Stéphane Chazelas

Cảm ơn bạn, cá nhân tôi đang sử dụng sh, nhưng câu trả lời của bạn có thể giúp đỡ người khác.
121391

4

với zsh:

shasum -- *(D.) > sums.sha1

Toàn cầu sẽ được mở rộng trước khi chuyển hướng được thực hiện, do đó sums.sha1sẽ không được bao gồm nếu nó không ở đó ngay từ đầu.

Dlà bao gồm các tệp chấm (tệp ẩn) như mong findmuốn. .là chỉ chọn các tệp thông thường (như của bạn -type f).

Để loại trừ sums.sha1dù sao đi nữa trong trường hợp nó đã ở đó ngay từ đầu:

setopt extendedglob # best in ~/.zshrc
shasum -- ^sums.sha1(D.) > sums.sha1

Lưu ý rằng những người chạy một lệnh shasum, vì vậy cuối cùng bạn có thể thấy lỗi "Danh sách đối số quá dài" nếu danh sách quá lớn. Để làm việc xung quanh đó:

autoload zargs
zargs -e/ -- *(D.) / shasum > sums.sha1

Tôi khuyên bạn nên sử dụng ./*thay vì *để tránh các vấn đề tiềm ẩn với một tệp được gọi -.


Tôi đã chỉnh sửa câu hỏi với loại vỏ, nhưng câu trả lời của bạn nhắc tôi rằng tôi muốn chuyển sang zsh một thời gian trước đây ...;)
user121391

1

Như các câu trả lời khác đã nêu vấn đề là shell mở và tạo sums.sha1tệp, trước khi thực hiện đường ống dẫn của bạn. Bạn có thể sử dụng chương trình spongelà một phần của moreutilsgói nhiều bản phân phối. Ngược lại với chuyển hướng shell spongesẽ đợi cho đến khi nhận được mọi thứ, trước khi mở tệp. Nó thường được sử dụng khi bạn muốn viết một tệp bạn đọc trong cùng một đường ống.

Trong trường hợp của bạn, nó được sử dụng như thế này:

$ find -maxdepth 1 -type f -printf '%P\n' |xargs shasum |sponge sums.sha1
$ cat sums.sha1
31836aeaab22dc49555a97edb4c753881432e01d  B
7d157d7c000ae27db146575c08ce30df893d3a64  A

0

Là một thay thế cho find / xargs, v.v. bạn có thể muốn sha1deep. Có lẽ nó nằm trong một gói khác - trên hộp của tôi, nó có trong gói md5deep.

Như những người khác đã nói sums.sha1 được tạo bởi shell ngay cả trước khi bắt đầu tìm thấy. Một thủ thuật với ! -name sums.sha1tới findsẽ làm việc, như ý

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum | grep -v ' sums\.sha1$' > sums.sha1
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.