ค้นหา | xargs shasum สร้างการตรวจสอบของไฟล์ checksum เอง (ก่อนกำหนด) และล้มเหลวเมื่อตรวจสอบ


10

ปัญหาของฉัน (ในสคริปต์ด้วย#!/bin/sh) มีดังนี้: ฉันพยายามตรวจสอบไฟล์ทั้งหมดในไดเรกทอรีเพื่อการเก็บถาวร ไฟล์ checksum (ในกรณีของฉัน sha1) ที่มีชื่อไฟล์ทั้งหมดควรอยู่ในไดเรกทอรีเดียวกัน ช่วยบอกว่าเรามีไดเรกทอรี~/testพร้อมไฟล์f1และf2:

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

ตอนนี้กำลังคำนวณ checksums ด้วย

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

ทำตามที่ฉันต้องการทุกอย่างแสดงรายการไฟล์ทั้งหมดของไดเรกทอรีปัจจุบันเท่านั้นและคำนวณผลรวม sha1 (maxdepth อาจเปลี่ยนแปลงได้ในภายหลัง) เอาต์พุตบน STDOUT คือ:

f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2

น่าเสียดายที่เมื่อพยายามบันทึกไฟล์ลงในไฟล์ด้วย

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

ไฟล์ผลลัพธ์แสดงเช็กซัมสำหรับตัวมันเอง:

da39a3ee5e6b4b0d3255bfef95601890afd80709  sums.sha1
f572d396fae9206628714fb2ce00f72e94f2258f  f1
9591818c07e900db7e1e0bc4b884c945e6a61b24  f2  

และดังนั้นจึงล้มเหลวในภายหลังshasum --checkเนื่องจากปัญหาที่ชัดเจนของการแก้ไขไฟล์เพิ่มเติมเมื่อบันทึกผลรวมสุดท้าย

ฉันมองไปรอบ ๆ และใช้การ-pตั้งค่าสถานะสำหรับxargsฉันพบว่ามันสร้างไฟล์เอาต์พุตก่อนที่จะดำเนินการคำสั่ง find ดังนั้นจึงพบไฟล์เพิ่มเติมและจะตรวจสอบ ...

ฉันรู้ว่าเป็นวิธีแก้ปัญหาฉันสามารถบันทึกการตรวจสอบไปยังสถานที่อื่น (ไดเรกทอรีชั่วคราวผ่านmktemp) หรือยกเว้นในการค้นหาโดยเฉพาะ แต่ฉันต้องการที่จะเข้าใจว่าทำไมมันทำงานในแบบที่มัน - ซึ่งอยู่ในสายตาของฉันไม่มีประโยชน์ เช่นถ้าคำสั่งแรกจะตรวจสอบว่าไฟล์ที่ส่งออกอยู่บนดิสก์แล้วมันจะไม่ได้คำตอบที่ถูกต้อง ...


8
มันไม่xargsได้เป็นเชลล์ตัวเองที่สร้างไฟล์นี้เพราะก่อนที่คำสั่งใด ๆ จะถูกดำเนินการในตอนแรกเชลล์เปลี่ยนเส้นทางอินพุตเอาต์พุตและไพพ์ทั้งหมดดังนั้นเมื่อfindเริ่มต้นไฟล์เอาต์พุตที่มีอยู่แล้ว ใช้-execแทน:find -maxdepth 1 -type f -exec sh -c 'shasum "$@" > sums.sha1' {} +
jimmij

@ jimmij, นั่นไม่รับประกันว่าจะทำงานได้เช่นกันหากshจำเป็นต้องมีการเรียกใช้หลายครั้ง ทราบว่าคุณต้องโต้แย้งสำหรับก่อน$0 {}
Stéphane Chazelas

@jimmij คำตอบอื่น ๆ ที่คุณแนะนำteeได้หายไป? ฉันพยายามมันและทำงานดีฉันยังเก็บกด STDOUT 1>/dev/nullกับการเพิ่มของ มีบางอย่างผิดปกติกับคำตอบหรือเป็นข้อผิดพลาดหรือไม่?
121391

@ user121391 Stephane ชี้ให้เห็นว่าบางครั้งอาจมีปัญหาสภาพการแข่งขันสิ่งที่ดูเหมือนจริง ฉันยกเลิกการลบไปซักพักเพื่อให้คุณสามารถดูได้ แต่ถ้าคุณมีหลายไฟล์ในรายการคำสั่งที่อาจผิดไป
jimmij

@jimmij อาฉันเห็น มันอาจจะมีประโยชน์ถ้าคุณนำหน้าด้วยคำเตือนเกี่ยวกับปัญหาเพราะฉันคิดว่ามันไม่ได้เป็นที่รู้จักกันดีว่าสิ่งนี้สามารถเกิดขึ้นได้ มิฉะนั้นฉันจะยอมรับคำตอบของคุณสำหรับกรณีที่การเรียกใช้ซ้ำรวมถึงไฟล์เก่าและ Anthon สำหรับกรณีที่ควรเขียนทับ
user121391

คำตอบ:


12

คุณสามารถป้องกันไฟล์จากการเข้าถึงxargsโดยใช้:

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

เพื่อป้องกันปัญหาเกี่ยวกับชื่อไฟล์ที่มีช่องว่างหรือขึ้นบรรทัดใหม่หรือคำพูดหรือแบ็กสแลชฉันจะใช้:

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

แทน.

คือการหลีกเลี่ยงปัญหาที่มีชื่อไฟล์ที่เริ่มต้นด้วย-- แต่มันจะไม่ช่วยให้สำหรับไฟล์ที่เรียกว่า- -หากคุณใช้-print0แทนคุณ-printf '%P\0'จะไม่จำเป็นต้องใช้--และจะไม่มีปัญหากับ-ไฟล์


ทางออกของคุณคือสิ่งที่ฉันใช้ ฉันชอบโดยเฉพาะอย่างยิ่งการเรียกใช้ที่ตามมาไม่ได้ทำใหม่ไฟล์การตรวจสอบและ inflat ไดเรกทอรี นอกจากนี้ในสคริปต์ของฉันฉันเคยbasenameได้รับชื่อไฟล์ sums.sha1 จากเส้นทางแบบเต็มที่กำหนด (ไม่รวมอยู่ในคำถาม แต่อาจช่วยคนอื่น ๆ )
121391

7

เนื่องจากคุณใช้-maxdepth 1ฉันถือว่าคุณไม่ต้องการเรียกซ้ำ ถ้าเป็นเช่นนั้นเพียงแค่ทำมันในเปลือกแทน:

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

หากต้องการข้ามไดเรกทอรีคุณสามารถทำได้:

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

หากคุณต้องการการสอบถามซ้ำและใช้งานbashให้ทำ:

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

โปรดทราบว่าวิธีการทั้งหมดนี้มีประโยชน์ในการทำงานกับชื่อไฟล์โดยพลการรวมถึงช่องว่างการขึ้นบรรทัดใหม่หรือสิ่งอื่นใด


ฉันคิดว่าคุณจะพูดถึงว่านี่แก้ปัญหาใด ๆ ที่ OP จะมีกับชื่อไฟล์ที่มีบรรทัดใหม่ในพวกเขาเช่นกัน ในทางตรงกันข้ามถ้าsums.sha1มีอยู่แล้ว (จากการเรียกใช้ก่อนหน้านี้) โซลูชันของคุณจะรวมเข้าด้วยกัน
Anthon

ขออภัยฉันไม่ได้ชี้แจงมาก่อน: maxdepth ใช้ในตัวอย่างนี้เท่านั้นฉันใช้ฟังก์ชั่นที่ผู้ใช้ / สคริปต์สามารถระบุค่าใด ๆ ได้แม้ว่าในขณะนี้ฉันต้องการความลึก 1 เท่านั้น
user121391

@ user121391 ดูคำตอบที่ปรับปรุงแล้วสำหรับวิธีแบบเรียกซ้ำ
terdon

โปรดทราบว่ามันจะพยายามตรวจสอบไฟล์ที่ไม่ปกติประเภทอื่น ๆ เช่นไปป์, อุปกรณ์ ... (และเชื่อมโยงไปยังไฟล์เหล่านั้น)
Stéphane Chazelas

ขอบคุณส่วนตัวที่ฉันใช้shแต่คำตอบของคุณอาจช่วยคนอื่นได้
user121391

4

ด้วยzsh:

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

วงกลมจะถูกขยายก่อนที่จะทำการเปลี่ยนเส้นทางดังนั้นsums.sha1จะไม่ถูกรวมหากไม่มีในตอนแรก

Dคือการรวมจุดไฟล์ (ไฟล์ที่ซ่อน) ตามที่findต้องการ .คือการเลือกไฟล์ปกติเท่านั้น (เช่นของคุณ-type f)

หากต้องการยกเว้นสิ่งsums.sha1ต่อไปในกรณีที่มีอยู่ในสถานที่แรก:

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

โปรดทราบว่าผู้ที่วิ่งหนึ่งคำสั่ง shasum ดังนั้นคุณอาจจบลงด้วยการเห็น "รายการ Arg ยาวเกินไป" ข้อผิดพลาดถ้ารายการเป็นอย่างมาก ในการหลีกเลี่ยงสิ่งต่อไปนี้:

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

ฉันจะแนะนำให้ใช้./*แทนเพื่อหลีกเลี่ยงปัญหาที่อาจเกิดขึ้นกับไฟล์ที่เรียกว่า*-


ฉันแก้ไขคำถามด้วยประเภทของเชลล์ แต่คำตอบของคุณเตือนฉันว่าฉันต้องการเปลี่ยนเป็น zsh เมื่อไม่นานมานี้ ... ;)
user121391

1

ตามที่คำตอบอื่น ๆ ระบุไว้ปัญหาคือเชลล์เปิดและสร้างsums.sha1ไฟล์ก่อนดำเนินการไพพ์ไลน์ของคุณ คุณสามารถใช้โปรแกรมspongeซึ่งเป็นส่วนหนึ่งของmoreutilsแพ็คเกจของการกระจายจำนวนมาก ตรงกันข้ามกับการเปลี่ยนเส้นทางของเชลล์spongeจะรอจนกว่าจะได้รับทุกอย่างก่อนเปิดไฟล์ โดยทั่วไปจะใช้เมื่อคุณต้องการเขียนไฟล์ที่คุณอ่านในขั้นตอนเดียวกัน

ในกรณีของคุณมันถูกใช้แบบนี้:

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

0

เป็นทางเลือกแทน find / xargs ฯลฯ คุณอาจต้องการ sha1deep มันอาจจะอยู่ในแพ็คเกจอื่น - บนกล่องของฉันมันมาในแพ็คเกจ md5deep

ดังที่คนอื่น ๆ บอกไว้ว่า sums.sha1 นั้นถูกสร้างขึ้นโดยเชลล์ก่อนที่การค้นหาจะเริ่มต้นขึ้น เคล็ดลับ! -name sums.sha1ในการที่findจะทำงานตามที่จะ

find -maxdepth 1 -type f -printf '%P\n' | xargs shasum | grep -v ' sums\.sha1$' > sums.sha1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.