ถอดรหัส GNU Coreutils (2018)
(maizure.org)- เป็นโปรเจกต์อธิบายระยะยาวที่อ่านไล่ตามยูทิลิตีต่าง ๆ ใน โครงสร้างซอร์สของ GNU coreutils 8.3 เหมาะสำหรับโปรแกรมเมอร์มือใหม่ที่ต้องการเรียนรู้การออกแบบเครื่องมือ command line
- ไม่ใช่เอกสารวิธีใช้งาน แต่เป็น สื่อช่วยอ่านซอร์สโค้ด โดยครอบคลุม namespace ของแต่ละคำสั่ง ลำดับการทำงาน การตัดสินใจด้านการออกแบบ อัลกอริทึม และสายวิวัฒนาการของ UNIX กับ Coreutils ยุคแรก
- coreutils มีโปรแกรม C ขนาดเล็กที่มีจุดประสงค์เดียวอยู่มาก จึงต้องอ่าน รูปแบบโค้ดที่ดูหยาบตามมุมมองสมัยใหม่ เช่น global variable, macro,
goto, ฟังก์ชันยาว ๆ ภายในบริบททางประวัติศาสตร์ - ลำดับการทำงานร่วมทั่วไปคือ initialization, option parsing, input processing, การเรียกใช้ system call และ failure handling โดยมี
getopt_long(),fts, helper ของ gnulib ปรากฏซ้ำ ๆ - ก่อน contribute หรือแก้ไข ควรตรวจสอบก่อนว่าสามารถทำซ้ำได้ด้วยเครื่องมือที่มีอยู่หรือไม่, จะทำลาย backward compatibility หรือไม่ และเบี่ยงเบนจาก POSIX มากเพียงใด
ขอบเขตของโปรเจกต์อธิบาย GNU coreutils
- เป็นโปรเจกต์ระยะยาวที่อธิบายยูทิลิตีทั้งหมดของ GNU coreutils 8.3
- กลุ่มผู้อ่านเป้าหมายคือ โปรแกรมเมอร์มือใหม่ ที่สำรวจการออกแบบ command-line utility
- เหมาะที่สุดสำหรับใช้ดูประกอบเป็นคำอธิบายพื้นหลังขณะอ่าน source code ของยูทิลิตีที่สนใจ
- เนื้อหานี้ไม่ใช่คู่มือสำหรับผู้ใช้ และควรดูวิธีใช้งานจริงจาก man pages
- โปรเจกต์ประกอบด้วยหลายช่วง
- Phase 1: ทำหน้า namespace และภาพรวมการทำงานของแต่ละยูทิลิตีให้เสร็จ
- Phase 2: ทำการตัดสินใจด้านการออกแบบหลักและอัลกอริทึม, สายวิวัฒนาการของ UNIX และ Coreutils ยุคแรก, walkthrough ซอร์สที่มีประโยชน์กว่าเดิม และ visualization วิวัฒนาการของซอร์สโค้ดให้เสร็จ
- Phase indefinite: walkthrough โค้ดแบบรายบรรทัดของแต่ละยูทิลิตียังคงเป็นงานระยะยาว
- มี GitHub repo สำหรับรวบรวมโน้ตรายบรรทัด
หน้าอธิบายตามยูทิลิตี
- ชื่อคำสั่งแต่ละคำสั่งลิงก์ไปยังหน้ารายละเอียดที่ถอดรหัสยูทิลิตีนั้น
- แต่ละหน้ามีการอภิปราย ซอร์สโค้ด และ walkthrough
- ยูทิลิตีที่แสดงเป็นตัวหนาคือรายการที่ขยายใน Phase 2
- รายการมีคำสั่ง GNU coreutils เช่น
arch,base64,cat,chmod,cp,date,dd,df,ls,rm,sort,tail,tr,wc,yesเป็นต้น
ข้อควรรู้ก่อนอ่านโค้ด
- ยูทิลิตี coreutils จำนวนมากมีประวัติศาสตร์เกือบ 30 ปี และเป็นโค้ดที่หลายคนแก้ไขมาเป็นเวลานาน
- ส่วนใหญ่เป็น โปรแกรมขนาดเล็ก ที่ปรับให้เหมาะกับจุดประสงค์เดียว
- โดยทั่วไปเป็นโปรแกรมที่มีไฟล์ซอร์สเดียว
- ไม่ใช่โค้ดที่ออกแบบมาเพื่ออยู่รอดยาวนานหรือขยายเกินบทบาทของมัน
- จะเห็นรูปแบบอย่าง global variable, macro,
goto, ฟังก์ชันยาว ๆ ที่มีswitch/loop ซ้อนกัน
- ความรู้ POSIX สำคัญต่อการเข้าใจโค้ด
- ควรเริ่มจาก Utility Syntax Guidelines
- POSIX นิยาม interoperability ของ input และ output แต่ปล่อยวิธีทำงานจริงให้ implementation
- GNU coreutils อาจไม่ได้ปฏิบัติตาม POSIX อย่างเคร่งครัด แต่แนวคิดอย่าง permission bit, uid/gid, environment variable และ exit status ฝังอยู่ลึกมาก
- เนื่องจากปัญหาด้าน portability coreutils จึงพึ่งพา gnulib อย่างมาก
- ยูทิลิตีแทบทั้งหมดรวมฟังก์ชันของ gnulib ที่ออกแบบมาเพื่อจัดการปัญหาร่วมกันบนหลายระบบ
- coreutils ทำงานโดยถือว่ามีการสนับสนุนจาก shell เช่น
bash,zsh,ksh- shell รันยูทิลิตีด้วย fork/clone, ส่ง argument, ตั้งค่า environment, redirect input/output ผ่าน pipe และเก็บค่า exit
- เดิม GNU coreutils เริ่มจากสามแพ็กเกจ ได้แก่ shell, text และ file utility และยูทิลิตีประเภทเดียวกันก็แชร์ pattern การออกแบบกันมาก
รูปแบบการออกแบบพื้นฐาน
- CLI utility ส่วนใหญ่มีสามขั้นตอนร่วมกัน
- ขั้นตอนตั้งค่า สำหรับเตรียม flag, option, localization ฯลฯ
- ขั้นตอน parse argument สำหรับอ่าน input และกำหนด execution parameter
- ขั้นตอนประมวลผลและรัน สำหรับเตรียม input แล้วส่งต่อไปยัง system call หนึ่งรายการหรือมากกว่า
- ระหว่างทำงานอาจตรวจสอบ constraint และล้มเหลวได้หลายจุด
- exit status ที่ต่างกันอาจบอกเป็นนัยว่าปัญหาอยู่ตรงไหน
EXIT_FAILUREมักใช้เป็นสถานะล้มเหลวทั่วไป
- หลังเกิดความล้มเหลว จะให้ feedback แก่ผู้ใช้
- รูปแบบย่อยของยูทิลิตีแบ่งเป็นสามกลุ่ม
- Trivial utilities: หลังนิยาม macro ไม่กี่บรรทัด จะ include ซอร์สของยูทิลิตีอื่น และใช้ macro บังคับ flow control เฉพาะ ตัวอย่างคือ
arch,dir,vdir - Wrapper utilities: หลังตั้งค่าและ parse option จะส่ง command-line option ไปเป็น argument ของ system call โดยตรง ผลของ system call จะเป็นผลลัพธ์ของยูทิลิตี และมีการประมวลผลเองน้อย ตัวอย่างคือ
link,whoami,hostid,logname - Full utilities: มีทั้งการตั้งค่า, การ parse option/argument, การประมวลผล input data และการเรียกใช้ system call หลายรายการ ยูทิลิตีส่วนใหญ่อยู่ในกลุ่มนี้
- Trivial utilities: หลังนิยาม macro ไม่กี่บรรทัด จะ include ซอร์สของยูทิลิตีอื่น และใช้ macro บังคับ flow control เฉพาะ ตัวอย่างคือ
การ initialize และ option parsing ที่ใช้ร่วมกัน
- ยูทิลิตีทั้งหมดมี ขั้นตอน initialization สั้น ๆ ช่วงต้นของ
main()initialize_main (&argc, &argv);set_program_name (argv[0]);setlocale (LC_ALL, "");bindtextdomain (PACKAGE, LOCALEDIR);textdomain (PACKAGE);atexit (close_stdout);
- โค้ด preprocessing นี้จัดการงานดูแล เช่น internationalization และการลงทะเบียนพฤติกรรมเมื่อจบโปรแกรม และไม่กระทบพฤติกรรมหลักของแต่ละยูทิลิตี
- ชุดเครื่องมือ Getopt มีบทบาทหลักในการ parse command-line option
- short option ใช้ prefix
-, long option ใช้ prefix-- - short option นิยามเป็น string ส่วน long option ใช้ struct
- ใน string ของ short option ถ้าไม่มี argument ให้ใส่แค่ตัวอักษร, argument บังคับใช้
:, argument เลือกได้ใช้:: - string ของ short option ใน
killคือLln:s:tหมายความว่าL,l,tไม่มี argument และn,sต้องมี argument - ในทุกยูทิลิตี
getopt_long()ใช้สำหรับคืนค่า option ถัดไป optindชี้ตำแหน่ง argument ถัดไปใน arrayargv[]optargเป็น character pointer ที่ชี้ไปยังค่าของ option argument
- short option ใช้ prefix
การเดินสำรวจไฟล์ซิสเต็มและ helper สำหรับ system call
- ระบบตระกูล Unix มักรองรับไลบรารี fts เพื่อจัดการการเดินสำรวจไฟล์ซิสเต็มได้ง่าย
- สร้างโครงสร้าง
FTSจาก path ด้วยfts_open()หรือxfts_open() - node ที่เป็นไฟล์หรือ directory ใน tree แทนด้วยโครงสร้าง
FTSENT - เมื่อเรียก
fts_read()จะสร้างFTSENTและกระบวนการนี้คือการเดินสำรวจ tree - ฟิลด์
FTSENT->fts_infoมักใช้ในการอธิบายรายการ และใช้กำหนดวิธีประมวลผลรายการนั้น
- สร้างโครงสร้าง
- coreutils ใช้ system call wrapper และ helper จำนวนมากนอกเหนือจากสิ่งที่ libc ให้มา
- ในการจัดการการเขียน นอกจาก wrapper ของ system call
write()และฟังก์ชัน libc อย่างfwrite()แล้ว ยังมีฟังก์ชันไม่เป็นมาตรฐานอย่างfull_write()ปรากฏด้วย- full_write() จะ retry การเขียนต่อไปตราบใดที่ไม่เกิด hard failure
- safe_write() จะ retry system call
write()แม้เกิด interrupt - ยังมี writing helper ที่ใช้เฉพาะยูทิลิตีเดียว เช่น
iwrite()ของdd,cwrite()ของsplit
ฟังก์ชันร่วมและ idiom ของโค้ด
- ยูทิลิตีทั้งหมดใช้ฟังก์ชันอย่างน้อย
main(),usage(),_() usage()แสดง help ที่มี input parameter, ความหมาย และ syntax ที่ถูกต้อง_()เป็น macro ที่นิยามในsystem.hและเชื่อม string ธรรมดากับความสามารถ Native Language Support ของ GNU gettext.h- หากเป็น string ที่แสดงให้ผู้ใช้เห็น โดยทั่วไปจะถูกครอบด้วย macro นี้
- ยูทิลิตีที่ไม่ใช่แบบง่ายมากส่วนใหญ่มีบรรทัดโค้ดร่วมกันซ้ำ ๆ
#include "system.h"นิยาม macro, variable และฟังก์ชัน utility ไม่เป็นมาตรฐานที่ขึ้นกับระบบPROGRAM_NAMEนิยามชื่อทางการของยูทิลิตีและใช้ในการตรวจสอบเวอร์ชันAUTHORSนิยามผู้เขียนยูทิลิตีและใช้ในการตรวจสอบเวอร์ชันemit_try_help()พิมพ์คำแนะนำให้ดู help หลังจาก output ที่ล้มเหลวemit_ancillary_info (PROGRAM_NAME)พิมพ์ข้อมูล help เพิ่มเติมร่วมกันหลัง output เฉพาะคำสั่งinitialize_main(&argc, &argv)จัดการการขยาย wildcard ในตัวของ VMS และถูกตัดทิ้งในระบบปฏิบัติการอื่นส่วนใหญ่atexit(close_stdout)ลงทะเบียนฟังก์ชันที่จะ flush และปิด buffered stream เมื่อโปรแกรมจบ
- ยังมี C idiom ที่มือใหม่อาจไม่คุ้นเคย
!!คือ unary NOT สองครั้ง ใช้บังคับแปลงค่าเป็น booleando { ... } while (0)เป็น pattern ที่ไม่ใช่ loop สำหรับครอบ macro หลาย statement เพื่อทำให้ tokenization ปลอดภัยหลัง preprocessor substitution
วิธีบำรุงรักษาและสิ่งที่ต้องตรวจสอบก่อน contribute
- โปรเจกต์ที่ active อย่าง coreutils โดยทั่วไปวิวัฒน์ผ่าน workflow การบำรุงรักษาสามแบบ
- การเปลี่ยนแปลงทั้งโปรเจกต์ คือการเปลี่ยนแปลงขนาดใหญ่ที่ครอบคลุมโครงสร้างและ dependency ของยูทิลิตีทั้งหมด
- ในปี 1995 มีการเพิ่ม Native Language Support ผ่าน GNU gettext project ทำให้มีการนำ macro
_()มาใช้กับบรรทัด output ข้อความส่วนใหญ่ - ในปี 1996 การรองรับ internationalization ถูกขยาย และมีโค้ด initialization หลายส่วนเพิ่มเข้าใน
main() - ในปี 1995 มีการเพิ่มคำอธิบายสั้น ๆ ของวัตถุประสงค์ยูทิลิตีใน output วิธีใช้
- ในปี 2003 มีการเพิ่มการรองรับ wildcard ของ VMS ซึ่งเห็นได้ในฟังก์ชัน
initialize_main() - ในปี 2016 macro
die()เข้ามาแทนฟังก์ชันexit()และerror()ส่วนใหญ่ เพื่อหลีกเลี่ยง compiler warning ในเส้นทางที่ล้มเหลว
- ในปี 1995 มีการเพิ่ม Native Language Support ผ่าน GNU gettext project ทำให้มีการนำ macro
- การเปลี่ยนแปลงรายยูทิลิตี แบ่งเป็น bug fix, feature ใหม่ และ optimization
- การบำรุงรักษารายปี อย่างน้อยรวมถึงการอัปเดตปีลิขสิทธิ์ของยูทิลิตีทั้งหมด
- ยังมีการเปลี่ยนแปลงเชิงดูแลอย่างการอัปเดตที่อยู่ FSF ซึ่งไม่กระทบการทำงาน
- ผู้ contribute ควรตรวจสอบ GNU project page, contribution guidelines, rejected features, mailing list archives ก่อน
- มีคำถามสามข้อที่ควรตรวจสอบก่อนเขียนโค้ด
- สามารถทำฟังก์ชันเดียวกันด้วยเครื่องมือที่มีอยู่ได้หรือไม่
- contribution จะทำลาย backward compatibility หรือไม่
- พฤติกรรมที่เสนอเบี่ยงเบนจาก POSIX มากหรือไม่
- หากไม่มั่นใจ แนะนำให้สอบถาม community ผ่าน mailing list
รายละเอียดที่น่าสนใจ
- ยูทิลิตีที่สั้นที่สุดคือ
falseมี 2 บรรทัด เท่ากับarch,dir,vdir - ยูทิลิตีแบบ standalone ที่สั้นที่สุดคือ
trueมี 80 บรรทัด และ เวอร์ชันแรก แทบจะใกล้เคียงโปรแกรม C ขั้นต่ำ - ยูทิลิตีที่ยาวที่สุดคือ
lsมี 5308 บรรทัด - ยูทิลิตีจำนวนมากย้อนกลับไปถึง Research UNIX ในทศวรรษ 1970 และบางส่วนย้อนถึง Multics
- บรรพบุรุษทางแนวคิดที่เก่าแก่ที่สุดคือคำสั่ง CTSS
LISTFราวปี 1963 ซึ่งภายหลังย่อเป็นlsLISTFเป็นหนึ่งใน 18 utility ดั้งเดิมใน 1962 design paper
- syntax เฉพาะตัวของ
ddชวนให้นึกถึง OS/360 job control language ช่วงต้นทศวรรษ 1960 sortเป็นยูทิลิตีเดียวที่ใช้ multithreadingfmtแสดงการ optimize บรรทัดและย่อหน้าโดยใช้ cost ของฟีเจอร์yesใช้ memory buffer แบบ page-aligned เพื่อให้ output ประสิทธิภาพสูงdfใช้ metadata ของอุปกรณ์ ส่วนduตรวจสอบไฟล์ทั้งหมด จึงทำให้dfเร็วกว่าcksumมี entry point สองจุดสำหรับการรันทั่วไปและการสร้างตาราง CRC-32echoไม่มีเงื่อนไขความล้มเหลว- การออกแบบของ
testและexprแตกต่างจากโครงสร้างยูทิลิตีทั่วไปอย่างมาก - เดิม
suเคยบำรุงรักษาอยู่ใน coreutils/shellutils
implementation ที่น่าดู
- shuf และ shred ใช้ ISAAC cipher) สำหรับเลขสุ่ม
- shuf ใช้ Reservoir sampling
- sum ใช้ checksum แบบ legacy ของ System V และ BSD
- expr ใช้การประเมินนิพจน์แบบ left-associative
- shred ใช้ Secure overwrite
- cksum คำนวณ CRC-32 checksum
- sort ใช้การดัดแปลง Merge sort
- factor เป็นยูทิลิตีที่ควรดูองค์ประกอบการ implement ทางคณิตศาสตร์
ข้อมูลการสนับสนุน
- ยังไม่ได้ตั้งค่ารับการสนับสนุนส่วนตัว
- หากต้องการแบ่งปันเวลาหรือเงิน สามารถ contribute ให้ Free Software Foundation ได้
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
แม้จะบอกว่า “ยูทิลิตีเหล่านี้จำนวนมากมีอายุเกือบ 30 ปีแล้ว และตลอดเวลานั้นก็มีคนจำนวนมากแก้ไขปรับปรุง” แต่ถ้าจะบอกว่า “ไม่ได้ถูกออกแบบมาให้ขยายออกไปเกินกว่าช่วงชีวิตอันยาวนานหรือบทบาทของมัน” ก็อยากเห็นตัวอย่างของ โปรแกรมที่ออกแบบโดยคำนึงถึงอายุการใช้งานยาวนาน ในมุมมองของผู้เขียน
อยากรู้ว่าเป็นโปรแกรมที่อยู่รอดมาได้ 30 ปีแล้ว หรือเป็นโปรแกรมที่คิดว่าน่าจะอยู่ต่อไปได้อีก 30 ปี
ผมเคยสร้างเฟรมเวิร์กทดสอบด้วย bash และ coreutils เพื่อทดสอบภาษาโปรแกรมเล็ก ๆ ที่ทำเอง ตอนแรกก็รู้สึกคาใจที่ไม่ได้ใช้ภาษา “จริงจัง” แต่ในทางปฏิบัติมันทำงานได้ดีมาก และรันแบบขนานได้ด้วย
สิ่งเดียวที่ทดสอบไม่ได้คือ
argv[0]ของโปรแกรม และไม่ว่าจะผสมวิธีไหนก็สร้างพฤติกรรมที่ต้องการได้ไม่ตรงเป๊ะ จึงส่งคำขอฟีเจอร์และแพตช์สำหรับenvไปยัง coreutils: https://lists.gnu.org/archive/html/coreutils/2023-08/msg0006...ดูเหมือนว่าจะถูกนำเข้า เท่ากับเป็นการเพิ่มฟีเจอร์ใหม่ให้โปรแกรมเก่า
แนวปฏิบัติที่ผู้เขียนยกมานั้น ในโปรแกรมขนาดใหญ่มักถูกวิจารณ์ว่าเมื่อสเกลใหญ่ขึ้นจะบั่นทอนความสามารถในการบำรุงรักษา
เอกสารที่น่าอ้างอิง:
วิธีทดสอบ GNU coreutils: https://www.pixelbeat.org/docs/coreutils-testing.html
สำรวจคำสั่งแต่ละตัวใน coreutils: https://ratfactor.com/slackware/pkgblog/coreutils
การประมวลผลข้อความบน command line ด้วย GNU Coreutils: https://learnbyexample.github.io/cli_text_processing_coreuti... — อีบุ๊กของผมที่ครอบคลุมเครื่องมือประมวลผลข้อความมากกว่า 20 ตัว
บทความที่เกี่ยวข้อง:
Decoded: GNU Coreutils (2018) - https://news.ycombinator.com/item?id=29871037 - มกราคม 2022, 7 ความคิดเห็น
Decoded: GNU coreutils (2019) - https://news.ycombinator.com/item?id=26411908 - มีนาคม 2021, 38 ความคิดเห็น
Decoded: GNU Coreutils - https://news.ycombinator.com/item?id=20328650 - กรกฎาคม 2019, 55 ความคิดเห็น
เรื่องน่าสนุกคือ ถ้าติดตั้ง coreutils บน macOS ด้วย Homebrew เพราะ macOS มี
od(1)อยู่แล้วodของ coreutils จึงถูกติดตั้งเป็นgod(1)ถ้าผู้เขียนกำลังอ่านอยู่ อย่างน้อยมีข้อผิดพลาดหนึ่งจุด คำอธิบายสั้น ๆ ของ หน้า shred[0] จริง ๆ แล้วเป็นคำอธิบายของ
csplit[1] และควรเป็นประมาณว่า “เขียนทับเพื่อซ่อนเนื้อหาไฟล์ และลบออกได้ตามต้องการ”[0]: https://maizure.org/projects/decoded-gnu-coreutils/shred.htm...
[1]: https://maizure.org/projects/decoded-gnu-coreutils/csplit.ht...
ไม่รู้มาก่อนว่ามีของแบบนี้อยู่ เจ๋งดี แม้แต่ของเรียบง่ายอย่าง
yesก็ค่อนข้างน่าสนใจในการดูว่า โค้ดพื้นฐานของยูทิลิตี ที่เขียนไปยัง stdout ถูกเขียนขึ้นอย่างไรhttps://maizure.org/projects/decoded-gnu-coreutils/yes.html
เช่น การมีตัวอย่างขั้นต่ำที่สอนวิธีเขียนเอาต์พุตให้เร็วมาก ๆ เป็นเรื่องดี โปรแกรมสั้นอยู่แล้ว จำนวนบรรทัดจึงไม่สำคัญมาก และถ้าผู้เขียนรู้วิธี ก็ควรทำให้เร็วที่สุดเท่าที่ทำได้เพื่อไม่ให้กลายเป็นตัวถ่วง
รายการยูทิลิตีพื้นฐานนี่ค่อนข้างน่าสนใจ ใช้ UNIX มานานมากแล้ว แต่ของอย่าง
shred,shuf,factorนี่ไม่เคยได้ยินมาก่อนชักอยากลองรัน
แล้วดูว่าจะไปได้ไกลแค่ไหนก่อนมันฆ่าตัวเอง แน่นอนว่าต้องบน เครื่องเสมือน หรืออุปกรณ์ที่แฟลชใหม่ได้ง่าย ๆ
ddแล้วมันทำงานจนจบ ซึ่งค่อนข้างกร่อยหวังว่าจะล่มหรืออย่างน้อยก็หลุดการเชื่อมต่อ แต่เคอร์เนลกับ
sshd,bashยังอยู่ในหน่วยความจำ เลยกลับมาที่พรอมป์ต์ที่แทบทำอะไรไม่ได้จริง ๆETXTBUSY) ดังนั้นshredจึงทำลายตัวมันเองหรือfindไม่ได้ชอบตรงที่
/bin/trueจริง ๆ แล้วอาจล้มเหลวและคืนค่า false ได้ ในเชิงเทคนิค การเรียก “Not/bin/false” ก็เลยแน่นหนากว่า: https://github.com/coreutils/coreutils/blob/master/src/true....แน่นอนว่ารู้ว่าเป็นกรณีที่แทบไม่เกิดขึ้น แต่ก็ขำดี
trueคือมันซับซ้อนเกินจำเป็นไปมากลองฝึกกันก่อน:
trueเริ่มต้นแบบนี้ตามตัวอักษรเลย ค่อนข้างเป็นปริศนาธรรมแบบเซนการเปลี่ยนแปลงแรกเกิดจากฝ่ายกฎหมาย โค้ดทุกไฟล์ต้องมีประกาศลิขสิทธิ์ และไฟล์ว่างก็ต้องมีด้วย ดังนั้นไฟล์ที่ไม่มีอะไรเลยจึงกลายเป็นไฟล์ที่มีแค่ประกาศลิขสิทธิ์ แล้วก็เกิดคำถามว่า “เราจดลิขสิทธิ์ความไม่มีอะไรได้ไหม?” AT&T เคยลองทำแล้ว
ต่อมามีคนบอกว่าโปรแกรมควรถูกนิยามไว้อย่างชัดเจน และไม่ควรพึ่งพาพฤติกรรมบังเอิญของ Unix ซึ่งในตอนนั้นก็สมเหตุสมผลทีเดียว ดังนั้นในที่สุด
trueก็มีโค้ด และโค้ดนั้นคือexit 0แล้วก็มีคนบอกอีกว่ายูทิลิตีระบบควรเขียนด้วย C แทนเชลล์เพื่อให้เร็วกว่า และใน OpenBSD ยังมีตัวอย่างที่ดีของรูปแบบนั้นอยู่: http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr....
ถึงจุดหนึ่ง ระบบราชการแบบ GNU ก็เข้ามาเกี่ยว โดยบอกว่าทุกโปรแกรมต้องรองรับแฟล็ก
-hและต่อมาก็บอกว่าทุกโปรแกรมต้องรองรับโลเคล ทำให้ GNUtrueทุกวันนี้ยาวถึง 80 บรรทัดอย่างน่าประหลาดใจ: https://github.com/coreutils/coreutils/blob/master/src/true....จะมองว่าก็โอเคก็ได้ แต่สำหรับโปรแกรมที่ตามนิยามคือ “ไม่ทำอะไรเลยและสำเร็จ” แล้ว ถือว่าโค้ดเยอะมาก
http://trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
trueเฉย ๆ ดูเหมือนจะไม่มีผล เว้นแต่คอมเมนต์จะทำให้เข้าใจผิดถ้าเป็นโปรแกรมเมอร์มือใหม่ที่อยากฝึกเซนส์ว่าจะนำโครงสร้างข้อมูลและอัลกอริทึมไปใช้อย่างมีประโยชน์ได้อย่างไร มีอะไรในนี้ที่ควรดูเป็นพิเศษไหม?
เช่น ใช้
copy_file_range()แทนread()/write()เพื่อหลีกเลี่ยงการคัดลอกข้อมูลมายัง user space อะไรแบบนี้ ใกล้เคียงวิศวกรรมซอฟต์แวร์มากกว่าวิทยาการคอมพิวเตอร์ผมอาจพลาดใจความของไซต์นี้ไปก็ได้ แต่ไม่ได้มี man page หรือหน้า info สำหรับแต่ละตัวอยู่แล้วเหรอ?