Unix shell มีความเสถียรเพียงใด“ stdin / stdout APIs”?


20

grepping, awking, sedding และ piping เป็นกิจวัตรประจำวันของผู้ใช้ระบบปฏิบัติการที่คล้าย Unix อาจอยู่ในบรรทัดคำสั่งหรือภายในเชลล์สคริปต์ (เรียกรวมกันว่าตัวกรองจากนี้เป็นต้นไป)

ที่สำคัญเมื่อทำงานกับโปรแกรม "มาตรฐาน" Unix CLI และเชลล์บิวด์อิน (เรียกรวมกันว่าคำสั่งต่อจากนี้ไป) ตัวกรองต้องการรูปแบบที่คาดหวังอย่างแม่นยำสำหรับ stdin, stdout และ stderr ในแต่ละขั้นตอนของตัวกรองเพื่อให้ทำงานได้อย่างถูกต้อง ฉันเรียกรูปแบบที่คาดหวังนี้ของคำสั่งบางอย่างว่า API ของคำสั่งนี้มีดังต่อไปนี้

ในฐานะที่เป็นคนที่มีพื้นฐานการพัฒนาเว็บฉันเปรียบเทียบการรวบรวมข้อมูลและการประมวลผลข้อมูลทางเทคนิคกับการขูดเว็บซึ่งเป็นเทคนิคที่ไม่แน่นอนมากเมื่อใดก็ตามที่มีการเปลี่ยนแปลงเล็กน้อยในการนำเสนอข้อมูล

คำถามของฉันตอนนี้เกี่ยวข้องกับความมั่นคงของ Unix APIs คำสั่ง

  1. คำสั่งในระบบปฏิบัติการแบบ Unix ทำตามมาตรฐานอย่างเป็นทางการที่เกี่ยวข้องกับอินพุตและเอาต์พุตหรือไม่?
  2. มีกรณีในประวัติศาสตร์ที่มีการปรับปรุงคำสั่งที่สำคัญบางอย่างทำให้การทำงานของตัวกรองบางตัวที่สร้างขึ้นโดยใช้คำสั่งรุ่นเก่ากว่านั้นเสียหายหรือไม่
  3. มีคำสั่ง Unix ที่สุกเมื่อเวลาผ่านไปซึ่งเป็นไปไม่ได้ที่จะเปลี่ยนแปลงอย่างที่ตัวกรองบางตัวสามารถแตกได้หรือไม่?
  4. ในกรณีที่ตัวกรองอาจแตกเป็นครั้งคราวเนื่องจากการเปลี่ยนคำสั่ง APIs ฉันจะเป็นนักพัฒนาป้องกันตัวกรองจากปัญหานี้ได้อย่างไร

คำตอบ:


17

POSIX 2008 เป็นมาตรฐานมีส่วนอธิบาย"เชลล์และสาธารณูปโภค" โดยทั่วไปถ้าคุณยึดติดกับที่สคริปต์ของคุณควรจะพิสูจน์ในอนาคตที่ค่อนข้างยกเว้นอาจเป็นไปได้สำหรับการคัดค้าน แต่ที่เกิดขึ้นแทบจะค้างคืนดังนั้นคุณควรมีเวลามากมายในการปรับปรุงสคริปต์ของคุณ

ในบางกรณีที่รูปแบบเอาท์พุทสำหรับยูทิลิตี้เดียวแตกต่างกันอย่างกว้างขวางในแพลตฟอร์มและรุ่นมาตรฐาน POSIX อาจรวมถึงตัวเลือกโดยทั่วไปเรียกว่า-pหรือ-Pที่ระบุรูปแบบผลลัพธ์ที่รับประกันและคาดการณ์ได้ ตัวอย่างนี้คือtimeยูทิลิตี้ซึ่งมีการใช้งานที่แตกต่างกันอย่างกว้างขวาง หากคุณต้องการรูปแบบที่มีความเสถียร API / time -pส่งออกคุณจะใช้

หากคุณต้องการใช้ยูทิลิตีตัวกรองที่ไม่ได้มาตรฐาน POSIX แสดงว่าคุณอยู่ในความเมตตาของผู้จัดจำหน่าย / ผู้พัฒนาต้นน้ำเช่นเดียวกับที่คุณอยู่ในความเมตตาของนักพัฒนาเว็บระยะไกลเมื่อทำการคัดลอกเว็บ


12

ฉันจะพยายามตอบจากประสบการณ์ของฉัน

  1. คำสั่งไม่เป็นไปตามข้อกำหนดอย่างเป็นทางการ แต่พวกเขาปฏิบัติตามข้อกำหนดในการใช้และสร้างข้อความเชิงเส้น

  2. ใช่แน่นอน ก่อนสาธารณูปโภค GNU กลายเป็นมาตรฐาน de facto จำนวนมากของผู้ขายจะมีการส่งออกที่เล่นโวหารโดยเฉพาะอย่างยิ่งด้วยความเคารพและps lsทำให้เกิดความเจ็บปวดอย่างมาก วันนี้มีเพียง HP เท่านั้นที่ให้คำสั่งสุดล้ำ ในอดีตสาธารณูปโภคของ Berkeley Software Distribution (BSD) เป็นช่วงเวลาสำคัญในอดีต ข้อมูลจำเพาะ POSIX เป็นการหยุดพักในอดีต แต่ตอนนี้ได้รับการยอมรับอย่างกว้างขวาง

  3. คำสั่ง Unix ได้ครบกำหนดเมื่อเวลาผ่านไป ยังคงเป็นไปไม่ได้ที่จะทำลายสคริปต์ที่เขียนขึ้นสำหรับรุ่นที่เก่ากว่า คิดถึงแนวโน้มล่าสุดที่มีต่อ UTF-8 ในการเข้ารหัสไฟล์ข้อความ trการเปลี่ยนแปลงนี้จำเป็นต้องมีการเปลี่ยนแปลงระบบสาธารณูปโภคขั้นพื้นฐานเช่น ในอดีตข้อความง่าย ๆ มักเป็น ASCII เกือบทุกครั้ง (หรือบางอย่างใกล้เคียง) ดังนั้นตัวอักษรตัวพิมพ์ใหญ่จึงสร้างช่วงตัวเลขเช่นเดียวกับตัวอักษรตัวเล็ก ไม่เป็นความจริงอีกต่อไปกับ UTF-8 ดังนั้นจึงtrต้องยอมรับตัวเลือกบรรทัดคำสั่งที่แตกต่างกันเพื่อระบุสิ่งต่าง ๆ เช่น "ตัวพิมพ์ใหญ่" หรือ "ตัวอักษรและตัวเลข"

  4. หนึ่งในวิธีที่ดีที่สุดในการ "เพิ่มความทนทาน" ตัวกรองของคุณคือไม่ขึ้นอยู่กับรูปแบบข้อความเฉพาะ ตัวอย่างเช่นอย่าทำcut -c10-24ซึ่งขึ้นอยู่กับตำแหน่งของบรรทัด ใช้cut -f2แทนซึ่งจะตัดฟิลด์ที่ 2 คั่นด้วยแท็บ awkแบ่งบรรทัดอินพุตเป็น $ 1, $ 2, $ 3 ... ซึ่งเป็น white-space คั่นด้วยค่าเริ่มต้น ขึ้นอยู่กับแนวคิดระดับสูงเช่น "ฟิลด์" มากกว่าแนวคิดระดับล่างเช่นตำแหน่งคอลัมน์ นอกจากนี้ให้ใช้นิพจน์ทั่วไป: sedและawkสามารถทำสิ่งต่างๆด้วยนิพจน์ทั่วไปที่ไม่สนใจความแปรปรวนบางอย่างในอินพุต เคล็ดลับอีกอย่างคือการประมวลผลอินพุตเป็นสิ่งที่มีรูปแบบที่ตัวกรองของคุณสามารถเลือกได้ ใช้tr -cs '[a-zA-z0-9]' '[\n]'เพื่อแบ่งข้อความเป็นคำเดียวต่อบรรทัดโดยไม่มีเครื่องหมายวรรคตอน คุณเพิ่งจะ '


9

ก่อนตอบคำถามของคุณสั้นมาก:

  1. มาตรฐานอย่างเป็นทางการของอนุสัญญาอินพุท / เอาท์พุต: ไม่มี
  2. ความแตกแยกในอดีตเนื่องจากการเปลี่ยนแปลงผลลัพธ์: ใช่
  3. ไม่สามารถทำลายตัวกรองในอนาคตได้อย่างแน่นอน: ไม่
  4. ฉันจะป้องกันตนเองจากการเปลี่ยนแปลงได้อย่างไร : ระมัดระวัง

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

  • แต่ละบรรทัดอินพุตเป็นเร็กคอร์ดที่สมบูรณ์
  • ภายในแต่ละระเบียนเขตข้อมูลจะถูกคั่นด้วยอักขระตัวคั่นที่รู้จัก

ตัวอย่างคลาสสิกจะเป็นรูปแบบของ / etc / passwd แต่อนุสัญญาเริ่มต้นเหล่านี้อาจถูกละเมิดในระดับหนึ่งบ่อยกว่าที่พวกเขาทำตามจดหมาย

  • มีตัวกรองจำนวนมาก (มักเขียนใน awk หรือ perl) ที่แยกวิเคราะห์รูปแบบอินพุตหลายบรรทัด
  • มีรูปแบบอินพุตจำนวนมาก (เช่น / var / log / ข้อความ) ที่ไม่มีโครงสร้างฟิลด์ที่กำหนดไว้ชัดเจนและต้องใช้เทคนิคที่ใช้นิพจน์ทั่วไปทั่วไป

คำถามที่สี่ของคุณวิธีป้องกันตัวเองจากความผันแปรในโครงสร้างเอาท์พุทเป็นคำถามเดียวที่คุณสามารถทำได้

  • ในฐานะที่เป็น@ jw013 พูดว่าดูสิ่งที่มาตรฐาน posix พูด แน่นอน posix ไม่ได้ระบุคำสั่งทั้งหมดที่คุณต้องการใช้เป็นแหล่งอินพุต
  • หากคุณต้องการให้สคริปต์ของคุณสามารถพกพาได้ให้พยายามหลีกเลี่ยงความงี่เง่าของเวอร์ชันใด ๆ ของคำสั่งบางคำสั่งที่คุณไม่ได้ทำ ตัวอย่างเช่นคำสั่ง unix มาตรฐาน GNU หลายรุ่นมีนามสกุลที่ไม่เป็นมาตรฐาน สิ่งเหล่านี้อาจมีประโยชน์ แต่คุณควรหลีกเลี่ยงหากคุณต้องการความสะดวกในการพกพาสูงสุด
  • พยายามที่จะเรียนรู้ว่าชุดย่อยของคำสั่งการขัดแย้งและรูปแบบผลลัพธ์มีแนวโน้มที่จะมีเสถียรภาพในแพลตฟอร์ม น่าเสียดายที่ต้องมีการเข้าถึงหลายแพลตฟอร์มพร้อมกับเวลาเพราะความแตกต่างเหล่านี้จะไม่ถูกบันทึกลงที่ใด ๆ

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


5

ครอบคลุม 1) คำถามของคุณเท่านั้น

APIs ตามธรรมชาติสามารถเปลี่ยนแปลงได้ตามความประสงค์ของผู้สร้างและจะแตกซอฟต์แวร์ที่ต้องพึ่งพาในทุกภาษา ที่กล่าวว่าความคิดที่ดีในเครื่องมือที่ใช้ระบบปฏิบัติการยูนิกซ์I / O 'APIs' คือว่ามีจริงไม่มี (อาจจะ0x0aเป็นจุดสิ้นสุดบรรทัด) สคริปต์ที่ดีจะกรองข้อมูลด้วยเครื่องมือ Unix แทนที่จะสร้างมันขึ้นมา นั่นหมายความว่าสคริปต์ของคุณอาจแตกเนื่องจากข้อมูลจำเพาะของอินพุตหรือเอาต์พุตเปลี่ยนไป แต่ไม่ใช่เพราะรูปแบบ I / O (อีกครั้งไม่มีจริง ๆ ) ของเครื่องมือแต่ละรายการที่ใช้ในสคริปต์เปลี่ยนไป (เพราะสิ่งที่ไม่มีอยู่จริง ไม่สามารถเปลี่ยนแปลงได้)

จะผ่านรายการของเครื่องมือพื้นฐานมีไม่กี่ที่ฉันจะผลิตคุณลักษณะเมื่อเทียบกับตัวกรองเท่านั้น:

  • wc - พิมพ์จำนวนไบต์, คำ, บรรทัด - รูปแบบที่ง่ายมากจึงไม่น่าจะเปลี่ยนแปลงอย่างแน่นอนและไม่น่าจะมีการใช้งานในสคริปต์
  • diff - มีการพัฒนารูปแบบผลลัพธ์ที่แตกต่างกัน แต่ฉันไม่เคยได้ยินปัญหาใด ๆ เลย ยังไม่ได้ใช้ตามปกติโดยไม่มีการควบคุม
  • วันที่ - ตอนนี้ที่นี่เราต้องดูแลสิ่งที่เราผลิตโดยเฉพาะเกี่ยวกับตำแหน่งที่ตั้งของระบบ แต่รูปแบบเอาต์พุตเป็น RFC'ed เนื่องจากคุณไม่ได้ระบุด้วยตัวคุณเอง
  • แคล - อย่าพูดถึงมันฉันรู้ว่ารูปแบบเอาต์พุตแตกต่างกันมากในระบบ
  • ls , ใคร , w , ครั้งสุดท้าย - ฉันอดไม่ได้ที่คุณจะแยกวิเคราะห์ ls มันแค่ไม่ได้มีความหมาย ยิ่งไปกว่านั้นใครคือคนสุดท้ายเป็นผู้มีส่วนร่วมมากขึ้น หากคุณใช้มันในสคริปต์คุณต้องดูแลสิ่งที่คุณทำ
  • เวลาถูกชี้ให้เห็นในโพสต์อื่น แต่ใช่มันเหมือนกับ ls เพิ่มเติมสำหรับการใช้แบบโต้ตอบ / ท้องถิ่น และ bash builtin นั้นแตกต่างจากรุ่น GNU อย่างมากและรุ่น GNU นั้นมีบั๊กที่ไม่ได้แยกไว้เป็นเวลาหลายปี อย่าพึ่งมัน

นี่คือเครื่องมือที่ต้องการรูปแบบอินพุตเฉพาะเจาะจงมากกว่าการเป็นสตรีมไบต์:

  • bc , dc - เครื่องคิดเลข แล้วในสิ่งที่แฮ็คมากขึ้น (จริงๆแล้วฉันไม่ได้ใช้พวกเขาในสคริปต์) และน่าจะเป็นรูปแบบ I / O ที่เสถียรมาก

มีพื้นที่อื่นที่มีความเสี่ยงต่อการแตกสูงกว่ามากคืออินเตอร์เฟสบรรทัดคำสั่ง เครื่องมือส่วนใหญ่มีคุณสมบัติที่แตกต่างกันทั้งในระบบและในไทม์ไลน์ ตัวอย่างคือ

  • เครื่องมือทั้งหมดที่ใช้ regex - regex สามารถเปลี่ยนความหมายตามตำแหน่งที่ตั้งของระบบ (เช่น LC_COLLATE) และมีรายละเอียดปลีกย่อยและข้อมูลเชิงลึกจำนวนมากในการนำไปใช้ของ regex
  • อย่าใช้สวิตช์แฟนซี คุณสามารถใช้man 1p findตัวอย่างเช่นเพื่ออ่าน POSIX ค้นหา manpage แทน manpage ของระบบ ในระบบของฉันฉันต้องติดตั้ง manpages-posix

และแม้กระทั่งเมื่อใช้สวิตช์ดังกล่าวปกติจะมีข้อผิดพลาดจะไม่ถูกนำมาใช้อย่างละเอียดและทำให้ข้อมูลของคุณเป็นพิษ โปรแกรมส่วนใหญ่จะปฏิเสธที่จะทำงานกับสวิตช์ที่ไม่รู้จัก

โดยสรุปแล้วฉันจะบอกว่าเชลล์มีศักยภาพในการเป็นหนึ่งในภาษาแบบพกพามากที่สุด (มันเป็นแบบพกพาเมื่อคุณสคริปต์พกพา) เปรียบเทียบกับภาษาสคริปต์ที่คุณชื่นชอบซึ่งเกิดข้อผิดพลาดเล็กน้อยหรือโปรแกรมที่คุณโปรดปรานซึ่งจะรวบรวมเพื่อรวบรวม

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


1

มีเพียงมาตรฐานพฤตินัย IO เท่านั้น - ช่องว่างและเอาต์พุตที่คั่นด้วย null

สำหรับความเข้ากันได้เรามักจะกลับไปตรวจสอบหมายเลขรุ่นของตัวกรองแต่ละตัว ไม่ใช่ว่ามันจะเปลี่ยนไปมากนัก แต่เมื่อคุณต้องการใช้ฟีเจอร์ใหม่และยังต้องการให้สคริปต์ทำงานบนเวอร์ชั่นที่เก่ากว่าคุณต้อง "ifdef" อย่างใด ในทางปฏิบัติไม่มีกลไกการรายงานความสามารถบันทึกสำหรับการเขียนกรณีทดสอบด้วยตนเอง


0

สคริปต์ทำงานผิดพลาดบางครั้งก็บ่อยกว่าสคริปต์อื่น ๆ ซอฟต์แวร์ที่เก่าและมีชื่อเสียงมักจะอยู่ในระดับเดียวกันและมักจะมีค่าสถานะการทำงานร่วมกันเมื่อมีการเปลี่ยนแปลง

สคริปที่เขียนในระบบหนึ่งมักจะทำงานต่อไป แต่มักจะผิดพลาด

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