4 คะแนน โดย GN⁺ 2023-09-10 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เป็นโปรเจกต์อธิบายระยะยาวที่อ่านไล่ตามยูทิลิตีต่าง ๆ ใน โครงสร้างซอร์สของ 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 หลายรายการ ยูทิลิตีส่วนใหญ่อยู่ในกลุ่มนี้

การ 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 ถัดไปใน array argv[]
    • optarg เป็น character pointer ที่ชี้ไปยังค่าของ option argument

การเดินสำรวจไฟล์ซิสเต็มและ 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 สองครั้ง ใช้บังคับแปลงค่าเป็น boolean
    • do { ... } 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 ในเส้นทางที่ล้มเหลว
  • การเปลี่ยนแปลงรายยูทิลิตี แบ่งเป็น bug fix, feature ใหม่ และ optimization
    • join, sort, uniq เคยเสี่ยงต่อ overflow attack จนก่อน patch ปี 2016
    • df มีการ เพิ่ม option --output ในปี 2013
    • yes ได้รับการ ปรับปรุง ประสิทธิภาพด้วย buffering ที่ดีกว่าเดิม
  • การบำรุงรักษารายปี อย่างน้อยรวมถึงการอัปเดตปีลิขสิทธิ์ของยูทิลิตีทั้งหมด
    • ยังมีการเปลี่ยนแปลงเชิงดูแลอย่างการอัปเดตที่อยู่ 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 ซึ่งภายหลังย่อเป็น ls
    • LISTF เป็นหนึ่งใน 18 utility ดั้งเดิมใน 1962 design paper
  • syntax เฉพาะตัวของ dd ชวนให้นึกถึง OS/360 job control language ช่วงต้นทศวรรษ 1960
  • sort เป็นยูทิลิตีเดียวที่ใช้ multithreading
  • fmt แสดงการ optimize บรรทัดและย่อหน้าโดยใช้ cost ของฟีเจอร์
  • yes ใช้ memory buffer แบบ page-aligned เพื่อให้ output ประสิทธิภาพสูง
  • df ใช้ metadata ของอุปกรณ์ ส่วน du ตรวจสอบไฟล์ทั้งหมด จึงทำให้ df เร็วกว่า
  • cksum มี entry point สองจุดสำหรับการรันทั่วไปและการสร้างตาราง CRC-32
  • echo ไม่มีเงื่อนไขความล้มเหลว
  • การออกแบบของ test และ expr แตกต่างจากโครงสร้างยูทิลิตีทั่วไปอย่างมาก
  • เดิม su เคยบำรุงรักษาอยู่ใน coreutils/shellutils

implementation ที่น่าดู

ข้อมูลการสนับสนุน

  • ยังไม่ได้ตั้งค่ารับการสนับสนุนส่วนตัว
  • หากต้องการแบ่งปันเวลาหรือเงิน สามารถ contribute ให้ Free Software Foundation ได้

1 ความคิดเห็น

 
GN⁺ 2023-09-10
ความคิดเห็นบน Hacker News
  • แม้จะบอกว่า “ยูทิลิตีเหล่านี้จำนวนมากมีอายุเกือบ 30 ปีแล้ว และตลอดเวลานั้นก็มีคนจำนวนมากแก้ไขปรับปรุง” แต่ถ้าจะบอกว่า “ไม่ได้ถูกออกแบบมาให้ขยายออกไปเกินกว่าช่วงชีวิตอันยาวนานหรือบทบาทของมัน” ก็อยากเห็นตัวอย่างของ โปรแกรมที่ออกแบบโดยคำนึงถึงอายุการใช้งานยาวนาน ในมุมมองของผู้เขียน
    อยากรู้ว่าเป็นโปรแกรมที่อยู่รอดมาได้ 30 ปีแล้ว หรือเป็นโปรแกรมที่คิดว่าน่าจะอยู่ต่อไปได้อีก 30 ปี

    • ปัญหาคือบทบาทนั้นคืออะไรกันแน่ ผมสงสัยว่ามีขีดจำกัดกับสิ่งที่ทำได้ด้วยเครื่องมือเหล่านี้หรือไม่
      ผมเคยสร้างเฟรมเวิร์กทดสอบด้วย bash และ coreutils เพื่อทดสอบภาษาโปรแกรมเล็ก ๆ ที่ทำเอง ตอนแรกก็รู้สึกคาใจที่ไม่ได้ใช้ภาษา “จริงจัง” แต่ในทางปฏิบัติมันทำงานได้ดีมาก และรันแบบขนานได้ด้วย
      สิ่งเดียวที่ทดสอบไม่ได้คือ argv[0] ของโปรแกรม และไม่ว่าจะผสมวิธีไหนก็สร้างพฤติกรรมที่ต้องการได้ไม่ตรงเป๊ะ จึงส่งคำขอฟีเจอร์และแพตช์สำหรับ env ไปยัง coreutils: https://lists.gnu.org/archive/html/coreutils/2023-08/msg0006...
      ดูเหมือนว่าจะถูกนำเข้า เท่ากับเป็นการเพิ่มฟีเจอร์ใหม่ให้โปรแกรมเก่า
    • ตรงนี้น่าจะต้องเข้าใจว่าหมายถึง “ถูกสร้างมาให้ พัฒนาอย่างต่อเนื่อง ได้นาน”
      แนวปฏิบัติที่ผู้เขียนยกมานั้น ในโปรแกรมขนาดใหญ่มักถูกวิจารณ์ว่าเมื่อสเกลใหญ่ขึ้นจะบั่นทอนความสามารถในการบำรุงรักษา
    • คงหมายถึง “ช่วงชีวิตของการพัฒนาที่ยาวนาน” มากกว่า กล่าวคือเป็น ยูทิลิตีที่เขียนครั้งเดียวแล้วใช้ได้นาน และนี่ก็เป็นข้อดีของแนวทาง “ทำสิ่งเดียวให้ดี”
    • แปลกดีที่ผมตีความ “long life” ว่าเป็นโปรแกรมที่รันค้างอยู่นาน มองว่าเป็นความหมายที่ตรงข้ามกับเครื่องมือที่มีอินพุตชัดเจน รันสั้น ๆ และมีเอาต์พุตชัดเจน
    • Emacs?
  • เอกสารที่น่าอ้างอิง:
    วิธีทดสอบ 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

    • ประเด็นของผมคือ แม้จะเป็นยูทิลิตีเรียบง่าย ก็สมควรถูกเขียนและปรับให้เหมาะสมอย่างดี
      เช่น การมีตัวอย่างขั้นต่ำที่สอนวิธีเขียนเอาต์พุตให้เร็วมาก ๆ เป็นเรื่องดี โปรแกรมสั้นอยู่แล้ว จำนวนบรรทัดจึงไม่สำคัญมาก และถ้าผู้เขียนรู้วิธี ก็ควรทำให้เร็วที่สุดเท่าที่ทำได้เพื่อไม่ให้กลายเป็นตัวถ่วง
    • อิมพลีเมนต์ด้วย Go ที่ทำเล่น ๆ เป็นแบบนี้:
      package main
      
      import "flag"
      
      func main() {  
      yes := flag.String("m", "y", "message")  
      flag.Parse()  
      for {  
      println(*yes)  
      }  
      }  
      
  • รายการยูทิลิตีพื้นฐานนี่ค่อนข้างน่าสนใจ ใช้ UNIX มานานมากแล้ว แต่ของอย่าง shred, shuf, factor นี่ไม่เคยได้ยินมาก่อน
    ชักอยากลองรัน

    sudo find / -type f -exec shred {} \;  
    

    แล้วดูว่าจะไปได้ไกลแค่ไหนก่อนมันฆ่าตัวเอง แน่นอนว่าต้องบน เครื่องเสมือน หรืออุปกรณ์ที่แฟลชใหม่ได้ง่าย ๆ

    • เคยลองทำอะไรคล้าย ๆ กันด้วย dd แล้วมันทำงานจนจบ ซึ่งค่อนข้างกร่อย
      หวังว่าจะล่มหรืออย่างน้อยก็หลุดการเชื่อมต่อ แต่เคอร์เนลกับ sshd, bash ยังอยู่ในหน่วยความจำ เลยกลับมาที่พรอมป์ต์ที่แทบทำอะไรไม่ได้จริง ๆ
    • บน Linux น่าจะมีโอกาสไปจนจบสูง เพราะเขียนทับ ไฟล์ปฏิบัติการและไลบรารี ที่กำลังรันอยู่ไม่ได้ (ETXTBUSY) ดังนั้น shred จึงทำลายตัวมันเองหรือ find ไม่ได้
  • ชอบตรงที่ /bin/true จริง ๆ แล้วอาจล้มเหลวและคืนค่า false ได้ ในเชิงเทคนิค การเรียก “Not /bin/false” ก็เลยแน่นหนากว่า: https://github.com/coreutils/coreutils/blob/master/src/true....
    แน่นอนว่ารู้ว่าเป็นกรณีที่แทบไม่เกิดขึ้น แต่ก็ขำดี

    • อีกจุดที่น่าสนใจเกี่ยวกับคำสั่ง true คือมันซับซ้อนเกินจำเป็นไปมาก
      ลองฝึกกันก่อน:
      touch mytrue  
      chmod u+x mytrue  
      ./mytrue  
      echo "error code for mytrue is $?"  
      
      true เริ่มต้นแบบนี้ตามตัวอักษรเลย ค่อนข้างเป็นปริศนาธรรมแบบเซน
      การเปลี่ยนแปลงแรกเกิดจากฝ่ายกฎหมาย โค้ดทุกไฟล์ต้องมีประกาศลิขสิทธิ์ และไฟล์ว่างก็ต้องมีด้วย ดังนั้นไฟล์ที่ไม่มีอะไรเลยจึงกลายเป็นไฟล์ที่มีแค่ประกาศลิขสิทธิ์ แล้วก็เกิดคำถามว่า “เราจดลิขสิทธิ์ความไม่มีอะไรได้ไหม?” AT&T เคยลองทำแล้ว
      ต่อมามีคนบอกว่าโปรแกรมควรถูกนิยามไว้อย่างชัดเจน และไม่ควรพึ่งพาพฤติกรรมบังเอิญของ Unix ซึ่งในตอนนั้นก็สมเหตุสมผลทีเดียว ดังนั้นในที่สุด true ก็มีโค้ด และโค้ดนั้นคือ exit 0
      แล้วก็มีคนบอกอีกว่ายูทิลิตีระบบควรเขียนด้วย C แทนเชลล์เพื่อให้เร็วกว่า และใน OpenBSD ยังมีตัวอย่างที่ดีของรูปแบบนั้นอยู่: http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/usr....
      ถึงจุดหนึ่ง ระบบราชการแบบ GNU ก็เข้ามาเกี่ยว โดยบอกว่าทุกโปรแกรมต้องรองรับแฟล็ก -h และต่อมาก็บอกว่าทุกโปรแกรมต้องรองรับโลเคล ทำให้ GNU true ทุกวันนี้ยาวถึง 80 บรรทัดอย่างน่าประหลาดใจ: https://github.com/coreutils/coreutils/blob/master/src/true....
      จะมองว่าก็โอเคก็ได้ แต่สำหรับโปรแกรมที่ตามนิยามคือ “ไม่ทำอะไรเลยและสำเร็จ” แล้ว ถือว่าโค้ดเยอะมาก
      http://trillian.mit.edu/~jc/humor/ATT_Copyright_true.html
    • มันเป็นแบบนั้นเฉพาะตอนรันพร้อมออปชันไม่ใช่เหรอ? ถ้าแค่รัน true เฉย ๆ ดูเหมือนจะไม่มีผล เว้นแต่คอมเมนต์จะทำให้เข้าใจผิด
  • ถ้าเป็นโปรแกรมเมอร์มือใหม่ที่อยากฝึกเซนส์ว่าจะนำโครงสร้างข้อมูลและอัลกอริทึมไปใช้อย่างมีประโยชน์ได้อย่างไร มีอะไรในนี้ที่ควรดูเป็นพิเศษไหม?

    • ความฉลาดใน coreutils ส่วนใหญ่ไม่ได้อยู่ที่โครงสร้างข้อมูลและอัลกอริทึม แต่อยู่ที่วิธีทำงานร่วมกับเคอร์เนลอย่างมีประสิทธิภาพ
      เช่น ใช้ copy_file_range() แทน read()/write() เพื่อหลีกเลี่ยงการคัดลอกข้อมูลมายัง user space อะไรแบบนี้ ใกล้เคียงวิศวกรรมซอฟต์แวร์มากกว่าวิทยาการคอมพิวเตอร์
    • อาจเป็นเพราะผมเข้าใจผิดก็ได้ แต่ไม่รู้ว่า โครงสร้างข้อมูลและอัลกอริทึม เกี่ยวข้องอะไรกับเครื่องมือบรรทัดคำสั่ง
  • ผมอาจพลาดใจความของไซต์นี้ไปก็ได้ แต่ไม่ได้มี man page หรือหน้า info สำหรับแต่ละตัวอยู่แล้วเหรอ?

    • ไม่ใช่ข้อมูลวิธีใช้งาน แต่ไซต์นี้ลงรายละเอียดว่าโปรแกรม ทำงานภายในอย่างไร