ฉันจะจัดการข้อมูลไบนารีดิบในท่อทุบตีได้อย่างไร


15

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

echo -n '' >| "$file" #Truncate the file
while read lines
do  # Is there a better way to do this? I would like one...
    echo $lines >> "$file"
done

คำตอบ:


15

วิธีของคุณคือการเพิ่มตัวแบ่งบรรทัดในทุกสิ่งที่เขียนในช่องว่างของตัวคั่นใด ๆ ( $IFS) ที่ใช้เพื่อแยกการอ่านออก แทนที่จะแบ่งเป็นบรรทัดใหม่ให้ใช้ทั้งเรื่องและส่งต่อไป คุณสามารถลดรหัสบิตทั้งหมดด้านบนเป็น:

 cat - > $file

คุณไม่ต้องการบิตที่ตัดทอนนี่จะตัดทอนและเขียนสตรีม STDIN ทั้งหมดออกไป

แก้ไข:หากคุณใช้ zsh คุณสามารถใช้> $fileแทนแมวได้ คุณกำลังเปลี่ยนเส้นทางไปยังไฟล์และตัดทอนไฟล์ แต่หากมีสิ่งใดที่ค้างอยู่ที่นั่นรอสิ่งที่จะยอมรับ STDIN มันจะถูกอ่านในจุดนั้น ฉันคิดว่าคุณสามารถทำสิ่งนี้ได้ด้วยการทุบตี แต่คุณจะต้องตั้งค่าโหมดพิเศษบางอย่าง


ฉันไม่สามารถรับตัวอย่างการเปลี่ยนเส้นทาง stdin ให้ทำงานได้ แต่เปลี่ยนตัวอย่าง cat เป็น> | (ฉันมีชุด noclobber) ทำงานเหมือนมีเสน่ห์ ขอบคุณที่ทำวันของฉัน ^. ^
David Souther

+1 สำหรับรุ่น cat-less หลีกเลี่ยงแมวที่ไร้ประโยชน์เสมอ))
rozcietrzewiacz

@rozcietrzewiacz: จริงยกเว้นมันเป็นความคิดในภายหลังและฉันผิด นี่อาจไม่ใช่การใช้แมวที่ไร้ประโยชน์ > $fileสิ่งเดียวที่คุณอาจจะสามารถที่จะทำคือ สิ่งนี้ใช้งานได้เป็นสิ่งแรกที่มองหา stdin ในสคริปต์เชลล์หลัก โดยพื้นฐานแล้วรหัสทั้งหมดของดาวิดสามารถลดลงเป็นตัวละครเดียว แต่ผมคิดว่าcat -มันเป็นสิ่งที่สง่างามและมีปัญหาน้อยกว่าเพราะมันเป็นที่เข้าใจ
คาเลบ

บางครั้งฉันรวมสี่หรือห้าcats เข้าด้วยกันเพื่อรบกวน UUOC fanatics
Michael Mrozek

@MichaelMrozek: บางครั้งฉันตั้งชื่อไฟล์ข้อมูลของฉันcatเพื่อให้คนที่ยืนยันในการใช้จำเป็นต้องทำยิมนาสติกจิตเพื่ออ่านรหัส ท่อที่มีชื่อเป็นเป้าหมายที่ดีเช่นกัน
คาเลบ

7

หากต้องการอ่านไฟล์ข้อความอย่างแท้จริงอย่าใช้ข้อความธรรมดาreadซึ่งประมวลผลเอาต์พุตในสองวิธี:

  • readตีความ\ว่าเป็นตัวละครหนี ใช้read -rเพื่อปิดการทำงานนี้
  • readแยกออกเป็นคำในตัวละครใน$IFS; ตั้งค่าIFSเป็นสตริงว่างเพื่อปิด

สำนวนปกติในการประมวลผลไฟล์ข้อความทีละบรรทัดคือ

while IFS= read -r line; do 

สำหรับคำอธิบายของสำนวนนี้ให้ดูที่เหตุใดจึงมีการwhile IFS= readใช้บ่อย ๆ แทนที่จะเป็นIFS=; while read..? .

ในการเขียนสตริงอย่างแท้จริงไม่เพียง แต่ใช้ธรรมดาechoซึ่งประมวลผลสตริงได้สองวิธี:

  • ในบางเชลล์echoประมวลผลแบ็กสแลช (เมื่อทุบตีมันขึ้นอยู่กับว่าxpg_echoมีการตั้งค่าตัวเลือก)
  • สตริงบางตัวถือเป็นตัวเลือกเช่น-nหรือ-e(ชุดที่แน่นอนขึ้นอยู่กับเชลล์)

printfวิธีแบบพกพาในการพิมพ์สตริงแท้จริงอยู่กับ (ไม่มีวิธีที่ดีกว่าในการทุบตีถ้าคุณรู้ว่าการป้อนข้อมูลของคุณไม่ได้ดูเหมือนตัวเลือกecho) ใช้แบบฟอร์มแรกเพื่อพิมพ์สตริงที่แน่นอนและรูปแบบที่สองหากคุณต้องการเพิ่มขึ้นบรรทัดใหม่

printf %s "$line"
printf '%s\n' "$line"

สิ่งนี้เหมาะสำหรับการประมวลผลข้อความเท่านั้นเนื่องจาก:

  • เปลือกหอยส่วนใหญ่จะสำลักตัวอักษรว่างในอินพุต
  • เมื่อคุณอ่านบรรทัดสุดท้ายคุณไม่มีทางรู้ว่ามีการขึ้นบรรทัดใหม่ตอนท้ายหรือไม่ (เชลล์ที่เก่ากว่าบางตัวอาจมีปัญหามากขึ้นหากอินพุตไม่ได้ขึ้นบรรทัดใหม่)

คุณไม่สามารถประมวลผลข้อมูลไบนารีในเชลล์ได้ แต่ยูทิลิตี้รุ่นทันสมัยใน Unices ส่วนใหญ่สามารถรับมือกับข้อมูลที่กำหนดเองได้ catที่จะผ่านเข้าทั้งหมดผ่านการส่งออกการใช้ การไปแทนเจนต์echo -n ''เป็นวิธีที่ไม่ยุ่งยากในการพกพา echo -nจะดีเหมือนกัน (หรือไม่ขึ้นอยู่กับเปลือก) และ:ง่ายและพกพาได้อย่างเต็มที่

: >| "$file"
cat >>"$file"

หรือง่ายกว่า

cat >|"$file"

ในสคริปต์คุณมักไม่จำเป็นต้องใช้>|เนื่องจากnoclobberถูกปิดใช้งานตามค่าเริ่มต้น


ขอบคุณที่ชี้ให้เห็นว่า xpg_echo นั่นเป็นปัญหาที่ฉันมีในรหัสของฉันและไม่รู้ตัวเลย ไม่เป็นไรฉันอยู่ในนิสัยเปิดมันใน bashrc ของฉัน
David Souther

0

สิ่งนี้จะทำสิ่งที่คุณต้องการ:

( while read -r -d '' ; do
    printf %s'\0' "${REPLY}" ;
  done ;

  # When read hits EOF, it returns non-zero which exits the while loop.
  # That data still needs to be output:
  printf %s "${REPLY}"
) >> ${file}

อย่าจดบันทึกการใช้หน่วยความจำแม้ว่า สิ่งนี้อ่านอินพุตในรูปแบบที่คั่นด้วย null

หากไม่มีอินพุต\0 ว่างในไบต์แล้ว bash จะต้องอ่านเนื้อหาทั้งหมดของอินพุตลงในหน่วยความจำก่อนแล้วจึงส่งออก

เกี่ยวกับขั้นตอนการตัดปลายของคุณ:

echo -n '' >| "$file" #Truncate the file

ง่ายกว่าและเทียบเท่าคือ:

> ${file}   #Truncate the file
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.