2 คะแนน โดย GN⁺ 2024-06-24 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • ผู้ใช้ Unix ที่ใส่สคริปต์ส่วนตัวไว้ใน ~/bin/ แล้วเพิ่มลงใน PATH อาจเจอชื่อคำสั่งสั้น ๆ ชนกับ คำสั่งของระบบ ใหม่ได้
  • ในสภาพแวดล้อมที่มีคำสั่งให้มาจำนวนมากอย่าง Debian/Ubuntu ความเสี่ยงยิ่งสูงขึ้น โดยตัวอย่างโน้ตบุ๊ก Ubuntu เครื่องหนึ่งนับคำสั่งใน /usr/bin ได้ 21,733 รายการ
  • หากเติม เครื่องหมายจุลภาค (,) ไว้หน้าคำสั่งส่วนตัว เชลล์และเครื่องมือต่าง ๆ จะมองว่าเป็นอักขระธรรมดาในชื่อไฟล์ ขณะเดียวกันก็แยกจากคำสั่งของระบบได้ง่าย
  • เครื่องหมายจุลภาคพิมพ์ได้โดยไม่ต้องกด Shift และมีโอกาสชนต่ำกว่าวงเล็บ แบ็กสแลช โคลอน แบ็กทิก อัญประกาศเดี่ยว สแลช และจุด ซึ่งล้วนมีความหมายพิเศษในเชลล์
  • กด Tab หลัง , เพื่อไล่ดูรายการคำสั่งส่วนตัวได้ทันที ทำให้ดูแลชื่อคำสั่งใน ~/bin/ ให้เป็นระเบียบได้ง่าย

ทำไมชื่อคำสั่งส่วนตัวถึงชนกัน

  • ผู้ใช้ Unix จำนวนมากจะสร้าง ~/bin/ ไว้ในโฮมไดเรกทอรีและเพิ่มลงใน PATH เพื่อใช้คำสั่งอำนวยความสะดวกและเชลล์สคริปต์ของตัวเอง
  • ปัญหาคือชื่อสคริปต์ส่วนตัวมักเป็น ตัวพิมพ์เล็กสั้น ๆ จึงคล้ายกับคำสั่งระบบพื้นฐานได้ง่าย
  • เมื่อดิสทริบิวชัน Linux เพิ่มคำสั่งใหม่ ชื่อของมันอาจไปตรงกับคำสั่งส่วนตัวเดิมโดยบังเอิญ
  • ในสภาพแวดล้อมสาย Debian ที่มีคำสั่งมาให้จำนวนมาก ปัญหานี้ยิ่งเกิดขึ้นได้จริงมากขึ้น
    • หากนับจำนวนคำสั่งใต้ /usr/bin โดยตรงบนโน้ตบุ๊ก Ubuntu ตัวอย่างเครื่องหนึ่ง จะได้ 21,733 รายการ
    • apt-file search -x '^/usr/bin/[^/]*$' | wc -l

ข้อดีของการใส่จุลภาคเป็นคำนำหน้า

  • วิธีแก้คือเปลี่ยนชื่อคำสั่งส่วนตัวให้พิมพ์ง่าย แต่ก็ไม่ใช่รูปแบบที่มักถูกเลือกไปเป็นชื่อคำสั่งของระบบ
  • เกณฑ์ด้านความสะดวกในการพิมพ์คือ ไม่ต้องใช้ปุ่ม Shift และเมื่อกำหนดเงื่อนไขนี้แล้ว อักขระที่ปลอดภัยก็เหลือไม่มาก
    • ตัวพิมพ์เล็กถูกใช้ในคำสั่งระบบอยู่แล้วอย่างแพร่หลาย
    • วงเล็บ แบ็กสแลช โคลอน แบ็กทิก และอัญประกาศเดี่ยว มีความหมายพิเศษในเชลล์
    • สแลชใช้เป็นตัวคั่นไดเรกทอรี จึงใช้ในชื่อไฟล์ไม่ได้
    • จุดเมื่ออยู่หน้าชื่อไฟล์หมายถึงไฟล์ซ่อน และในตำแหน่งอื่นก็มักใช้แยกนามสกุลไฟล์
  • ตัวเลือกที่เหลือจึงเป็น เครื่องหมายจุลภาค (,) แบบตรงไปตรงมา ซึ่งเครื่องมือรอบข้างและเชลล์จะมองเป็นอักขระธรรมดาในชื่อไฟล์
  • หากเติมจุลภาคเป็นคำนำหน้าให้ทุกคำสั่งส่วนตัว ก็จะแยกจากคำสั่งระบบได้ชัดเจนและเลี่ยงปัญหาชื่อชนกันได้ง่าย
  • เมื่อนำไปใช้ร่วมกับ Tab completion ก็เพียงพิมพ์ , แล้วตรวจดูรายการคำสั่งส่วนตัวได้ทันที
    • รายการตัวอย่างมี ,complete-scp, ,complete-ssh, ,coreoff, ,coreon, ,find, ,go-thpgp, ,gr, ,hss, ,mount-thpgp, ,mount-twt, ,range, ,svn-store-password, ,umount เป็นต้น
  • วิธีนี้ถูกใช้งานมาราว 10 ปีแล้ว และแนะนำว่าเป็นแนวทางที่ดีในการจัดระเบียบชื่อคำสั่งใน ~/bin/ ส่วนตัวให้สะอาดเรียบร้อย

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

 
GN⁺ 2024-06-24
ความคิดเห็นจาก Hacker News
  • ตอนเห็นแค่ชื่อเรื่อง นึกว่าจะเป็นไอเดียที่แย่มาก แต่พอเอาเข้าจริงกลับค่อนข้างชอบ โดยเฉพาะส่วนที่ใช้ Tab แสดงรายการเครื่องมือของตัวเองทั้งหมด
    ช่วงหลัง ๆ ผมไม่ได้เจอปัญหา namespace ชนกันมากนัก และก็จริงที่หลังจากย้ายไปสายบริหารแล้ว ความรู้สึกทางเทคนิคก็หย่อนลงไปบ้าง สแต็กเทคโนโลยีของผมให้ความรู้สึกว่า ล้าหลังไปราว 10 ปี เลยสงสัยว่าถ้าจะกลับมาตามให้ทันความรู้สึกยุคใหม่ ควรเริ่มจากตรงไหนดี

    • วิธีที่เจ๋งในยุคนี้ดูเหมือนจะเป็นการสร้างอะไรสักอย่างเองในเวลาว่าง แล้วเรียนรู้ไปตามธรรมชาติระหว่างที่สร้าง การพึ่งพาตัวเอง, ความคิดสร้างสรรค์ และท่าทีที่เริ่มลงมือเองคือความรู้สึกของยุคนี้ และยิ่งสร้างมากเท่าไร ก็ยิ่งมีแรงจูงใจที่จะค้นพบวิธีรับมือปัญหาจริงและแนวทางแก้ปัญหาสมัยใหม่ด้วยตัวเองมากขึ้น
    • ผมก็อยู่สายบริหารเหมือนกัน และรู้สึกว่าถ้าไม่ตามต่อไป ทักษะทางเทคนิคคงถดถอย
      วิธีที่ผมทำคือสร้างเครื่องมือที่ทำให้ชีวิตตัวเองสะดวกขึ้นเอง เช่น ถ้าที่บริษัทมีเว็บเซอร์วิสที่ใช้ค้นข้อมูลง่าย ๆ บ่อย ๆ ผมก็จะดูว่ามี API ไหม แล้วเขียน CLI เพื่อให้งานประจำวันเร็วขึ้น ปรับแต่งจนพอใจแล้วค่อยแชร์ให้ทีม แต่การโน้มน้าวให้คนอื่นลองใช้นั้นยากอยู่ดี ถึงอย่างนั้นผมก็ใช้มันทุกวัน เลยไม่ได้กังวลมากนัก
    • ด้วยเหตุผลเดียวกัน ผมทำ side project ด้วยสแต็กแยกที่กำลังนิยมกว่า แทนที่จะใช้สแต็กที่คุ้นเคยจากงาน
      เป้าหมายคือเพื่อเห็นมุมมองใหม่ ๆ และคุยเรื่องเทรนด์ได้ ซึ่งบางส่วนตอนนี้ก็ถูกนำมาใช้ในงานแล้วด้วย ทำให้ผมเข้าใจคอมโพเนนต์เก่า ๆ ได้ลึกขึ้นด้วย
  • ผมไม่ค่อยเข้าใจปัญหานี้ แค่วางไดเรกทอรี bin ของผมไว้หน้าสุดของ $PATH แทนที่จะเป็นท้ายสุดก็พอ ถ้าจะดูคำสั่งของตัวเองก็แค่รัน ls ~/bin

    • แล้วก็อาจมีเครื่องมือบางตัวที่คาดว่า $0 อยู่ในพาธของระบบ แต่พอมันพังขึ้นมา ก็จะเริ่มการดีบักที่น่าหงุดหงิด
      สุดท้ายก็เป็นเรื่องว่าจะเลือกยาพิษฝั่งไหน
    • แค่จำชื่อที่ตัวเองตั้งไว้ก็พอไม่ใช่เหรอ? ผมไม่เข้าใจว่าทำไมเรื่องนี้ถึงถูกมองเป็นเหมือน การแฮ็ก แบบหนึ่ง
    • ข้อดีอย่างหนึ่งคือใช้ autocomplete ของ fzf ได้ เช่น ใน fish คุณพิมพ์ตัวอักษรแรกของคำสั่งแล้วกด Tab เพื่อเรียก fzf ขึ้นมาได้
      แบบนั้นแค่ ,+Tab ก็กรองคำสั่งที่ผู้ใช้กำหนดเองได้เร็วมาก ในขณะที่ ls ~/bin ต้องพิมพ์หลายตัวเกินไปสำหรับงานที่ทำบ่อย หรือไม่ก็อาจต้องใช้ ls+ลูกศรขึ้นหลายครั้งเพื่อหา autocomplete ก่อน
    • ls ~/bin พิมพ์ช้ากว่า , มาก
  • ผมใช้ชื่อคำสั่งที่กำหนดเองแบบสั้น ๆ เช่น aa, st, di, dp, cm, le สำหรับ wrapper บาง ๆ ที่ครอบ git
    หนึ่งในนั้นชนกับยูทิลิตีที่ติดตั้งมาเป็นค่าเริ่มต้นจริง ๆ ในบางระบบ แต่ไดเรกทอรี bin ของผมอยู่หน้าไดเรกทอรีของระบบใน $PATH ของผม ของผมเลยชนะ และผมก็ไม่ได้สนใจเครื่องมือที่ชนกันนั้นเท่าไร ถ้าเครื่องมืออื่นที่มีประโยชน์กับผมมาชนกับเครื่องมือของผม ผมน่าจะตั้ง alias ที่ไม่ชนให้เครื่องมืออื่นนั้น มากกว่าจะเปลี่ยนชื่อเครื่องมือของตัวเอง เครื่องมือสองตัวอักษร เหล่านี้สะดวกเกินไป

    • วิธีแบบนี้อาจนำไปสู่ปัญหาแบบที่ apt-get upgrade ไปเรียก Dwarf Fortress ขึ้นมา
      https://askubuntu.com/questions/938606/dwarf-fortress-starti...
    • ใช่ ชื่อยาว 1–3 ตัวอักษรควรเว้นไว้สำหรับ alias, ฟังก์ชัน, สคริปต์ของผู้ใช้ และยูทิลิตีมาตรฐาน
    • alias ของ git ของผมส่วนใหญ่ทำเป็นชุดสองตัวอักษรที่ขึ้นต้นด้วย g เช่น gs คือ git status
      แต่บางครั้งก็ต้องใช้ GhostScript จริง ๆ เหมือนกัน มันยอดเยี่ยมมากเวลาต้อง embed ฟอนต์ในไฟล์ PDF ปกติกรณีนั้นผมจะใช้ env gs
    • คำสั่งส่วนตัวของผมทั้งหมดขึ้นต้นด้วย j ตอน java โผล่มาก็เลยสนุกขึ้นพอสมควร การใช้เครื่องหมายจุลภาคเป็นไอเดียที่ค่อนข้างน่าสนใจ
      แต่ก็ยังดีที่ไม่ได้ขึ้นต้นด้วย k เพราะ KDE :)
    • อาจเป็นความเห็นที่ค่อนข้างแรง แต่ผมคิดว่าคำสั่งของระบบไม่ควรเข้าถึงได้ง่ายเท่าคำสั่งของผู้ใช้ ควรมี namespace ในรูปแบบใดรูปแบบหนึ่ง
      เช่น ผมคิดว่า mkfs ควรถูกเรียกแบบ sys::mkfs อะไรทำนองนั้น เส้นแบ่งระหว่างคำสั่งระบบกับคำสั่งผู้ใช้สามารถกำหนดได้หลายแบบ และก็คงมีส่วนที่คลุมเครืออยู่ แต่ถ้าผู้ใช้สามารถเผลอรันคำสั่งที่ไม่รู้ด้วยซ้ำว่ามีอยู่ และไม่เคยติดตั้งเองอย่างชัดเจน คำสั่งนั้นก็ไม่ควรถูกเปิดเผยอยู่ใน global namespace โดยตรง
  • มีคำถามที่เกี่ยวข้องอยู่
    ผมใช้ Windows เป็นส่วนใหญ่ และเหมือนกับผู้เขียน ผมเขียนสคริปต์ CLI ที่เน้น Python ไว้หลายตัว แล้ววางไว้ในตำแหน่งเทียบเท่า ~/bin/ พอตั้ง python.exe ให้เป็นโปรแกรมเริ่มต้นของส่วนขยาย .py และเพิ่ม .py เข้าไปใน %pathext% ก็สามารถพิมพ์แค่ hello จากพาธไหนก็ได้เพื่อรัน ~/bin/hello.py และใช้วันละหลายร้อยครั้ง ช่วงนี้ผมใช้ Linux มากขึ้น แต่ยังเป็นมือใหม่ เลยยังทำแบบเดียวกันไม่ได้ ใน Linux ดูเหมือนจะไม่มีแนวคิด “โปรแกรมที่เชื่อมโยงไว้” จึงเรียกไฟล์ .py ตรง ๆ แล้วให้เชลล์รันด้วย Python ไม่ได้ แน่นอนว่าสามารถ chmod +x ให้สคริปต์ได้ แต่แบบนั้นต้องใส่ shebang ไว้ในตัวสคริปต์เอง รู้สึกเหมือนฮาร์ดโค้ดจึงไม่ค่อยสบายใจ ถ้าภายหลังอยากรันสคริปต์ .py ด้วย /usr/bin/nohtyp แทน /usr/bin/python จะทำอย่างไร อีกอย่าง ผมก็ยังหาวิธีละส่วน .py ตอนเรียกสคริปต์ไม่ได้ด้วย ผมไม่ได้จะวิจารณ์การออกแบบของ Linux และรู้ว่ามันมีข้อดีมากมาย แต่ผมอยากรัน hello.py ที่อยู่ใน $PATH ด้วยชื่อ hello จริง ๆ

    • ใน Linux ผมยังมองว่า shebang เป็นเครื่องมือที่เหมาะกับปัญหานี้ ถ้าอยากทำแบบเบา ๆ ก็วาง symlink ชื่อ my_python ไว้ในพาธ แล้วเขียน shebang เป็น /usr/bin/env my_python ก็ได้
      ถ้าต้องการแนวทางที่เป็นหลักการกว่านี้ ให้ดูเครื่องมือ update-alternatives มันให้ abstraction แบบนี้ในลักษณะทั่วไปกว่า: https://linuxconfig.org/how-to-set-default-programs-using-up...
    • ทางแก้คนอื่นพูดไปแล้ว แต่ผมอยากเสริมเหตุผลอีกอย่าง
      ใน Linux หรือจริง ๆ แล้วในแพลตฟอร์มส่วนใหญ่ที่ไม่ใช่ Windows ความหมายของ นามสกุลไฟล์ อ่อนกว่ามาก ไฟล์ปฏิบัติการทุกชนิดถูกกำหนดว่ารันได้หรือไม่ด้วยแฟล็กอย่าง +x ไม่ใช่นามสกุล ข้อดีคือแม้เปลี่ยนภาษา implementation ก็เขียนใหม่ได้โดยไม่ทำให้ฝั่งที่เรียกพัง นามสกุล .py มีความหมายกับโมดูลที่ import มาใช้เท่านั้น ถ้าเป็นสคริปต์ที่ถูกรัน ก็แค่ดู shebang เมื่อจำเป็น สคริปต์ที่แจกจ่ายภายนอกมักใช้ #!/usr/bin/env python ส่วนสคริปต์ที่อยู่ในแพ็กเกจของดิสโทรมักถูกเขียนทับเป็น #!/usr/bin/python เป็นต้น อีกอย่าง shebang ไม่รองรับหลายอาร์กิวเมนต์ แต่ GNU env รองรับอาร์กิวเมนต์ -S ที่เลียนแบบเรื่องนี้ได้ อย่างไรก็ดี ปัญหาความยาวของอาร์กิวเมนต์ยังคงอยู่
    • ก็แค่เอา .py ออกจากชื่อไฟล์ก็พอ เรียกว่า "hello" ได้ ไม่มีปัญหาเลย
      ผมนึกข้อเสียของ shebang ไม่ค่อยออก ถ้าอยากรันด้วยอินเทอร์พรีเตอร์อื่นจริง ๆ ก็ระบุชัด ๆ แบบ "nohtyp hello" ได้ ถ้ายังรู้สึกขัดใจมาก ก็สามารถกำหนด alias ในไฟล์เริ่มต้นของเชลล์ได้ เช่นใน bash ทำได้แบบ alias hello="python3 /path/to/hello.py" และถ้าตั้งใจจริง ๆ ก็เขียนสคริปต์สั้น ๆ ให้สร้าง alias แบบนี้อัตโนมัติสำหรับเนื้อหาในไดเรกทอรีที่กำหนดได้
    • แนวคิดแบบนั้นมีอยู่ แต่ไม่ได้อยู่ในไวยากรณ์ของเชลล์ ปกติเป็น ปัญหาระดับแอปพลิเคชัน ที่มอบให้เดสก์ท็อป/GUI จัดการ
      ในเชลล์สคริปต์ ปกติจะเพิ่ม shebang และทำให้ไฟล์รันได้ เพื่อประกาศไฟล์ปฏิบัติการไว้ในตัวสคริปต์เอง คิดว่า shebang เป็นเหมือนนามสกุลไฟล์ชนิดหนึ่งก็ได้ ถ้าหลังจาก chmod +x ./malware.py แล้ว ./malware.py ไม่ทำงาน ให้ตรวจดูพาธที่ shebang ชี้ไป หากอินเทอร์พรีเตอร์สามารถรันสคริปต์เป็นอาร์กิวเมนต์ทั่วไปได้ ก็สามารถทำพฤติกรรมคล้ายกันด้วยวิธีอย่าง xdg-open malware.py ได้ น่าจะเหมือนกับการดับเบิลคลิกในตัวจัดการไฟล์เริ่มต้น ตอนที่ใช้ Linux เป็นระบบปฏิบัติการเดสก์ท็อปหลัก ผมเคยมี alias ชื่อ xop แต่ใช้เฉพาะกับไฟล์ข้อมูลอย่างรูปภาพหรือเอกสารที่พฤติกรรมเริ่มต้นถูกต้องอยู่แล้ว ไม่แนะนำให้ตั้งโปรแกรมเริ่มต้นของสคริปต์ที่รันได้เป็นอินเทอร์พรีเตอร์ เพราะโดยทั่วไปคุณอาจอยากเปิดสคริปต์ในเอดิเตอร์แทนที่จะรันมัน ผมคิดว่า xdg-open เป็นเครื่องมือฝั่ง Gnome แต่ไม่ได้หมายความว่าเดสก์ท็อปอื่นใช้ไม่ได้ และผมก็เคยใช้ใน Xubuntu ด้วย ถ้าคุณอยากให้ไฟล์ Python ทุกไฟล์ถูกรันเป็นค่าเริ่มต้นแม้ในบริบท GUI จริง ๆ ก็ตั้งค่าเริ่มต้นแบบนั้นได้ แต่ man xdg-open อาจช่วยได้ ขอย้ำว่าไม่ใช่คำแนะนำที่ดี
    • แม้จะไม่ได้บรรลุเป้าหมายโดยตรง แต่ shebang ก็เป็นการฮาร์ดโค้ดแค่ครึ่งเดียว วิธีใช้ shebang ที่ “ถูกต้อง” คือ #!/usr/bin/env python แม้จะมีข้อควรระวังอยู่บ้างตาม https://unix.stackexchange.com/a/29620
      แบบนี้จะรัน python ตัวแรกที่เจอในพาธ ถ้าภายหลังอยากรันด้วย /usr/bin/nohtyp แทน /usr/bin/python ก็สร้าง symlink ชื่อ python ที่ชี้ไปยัง /usr/bin/nohtyp ไว้ในไดเรกทอรีที่ถูกค้นหาก่อน /usr/bin ได้ เช่นเพิ่ม ~/myCommandPreferences ไว้หน้าสุดของ $PATH
  • อีกวิธีในการหลีกเลี่ยงการชนกันใน $PATH คือ ตั้งชื่อไฟล์ปฏิบัติการให้ยาวมากจนไฟล์ปฏิบัติการอื่นไม่น่าจะใช้ แล้วใส่ alias สั้น ๆ ใน bashrc
    alias จะไม่กระทบไฟล์ปฏิบัติการที่ถูกเรียกจากภายในสคริปต์ และในสคริปต์ของผมเองก็ยังอ้างอิงด้วยชื่อยาวต่อไปได้ ข้อเสียคือจะไม่ได้ ความสะดวกของการเติมคำอัตโนมัติด้วย Tab ในระดับเดียวกัน ซึ่งส่วนนั้นยอดเยี่ยมจริง ๆ อีกอย่าง สคริปต์ที่ต้องถูก source ไม่ใช่รันเป็นโปรเซสลูก เช่นสคริปต์เปิดใช้งาน venv ของ Python ก็ยังอาจเกิดการชนกันได้ แต่กรณีแบบนั้นมีไม่บ่อย

    • ใน zsh การเติมคำอัตโนมัติแบบนั้นก็ทำได้ด้วย
  • วิธีขึ้นต้นด้วยจุลภาคเป็นเทคนิคที่พบได้ทั่วไปในชุมชน ตัวขยายข้อความ/การแทนที่ข้อความ ด้วย

    • ใช่เลย alias ใน vim ของผมส่วนใหญ่ก็ขึ้นต้นด้วย ,
  • ช่วงหลังผมลองดู ~/.local/bin/ แล้วพบว่ามีไฟล์ปฏิบัติการหลายสิบตัวที่จำไม่ได้ว่าเคยใส่ไว้
    ส่วนใหญ่เกี่ยวกับ pyside แต่ก็มีสคริปต์อื่นด้วย ผมต้องเปิดดูทีละไฟล์เพื่อจำว่าอันไหนผมเขียนเอง และอันไหนคนอื่นทำ ถ้าตั้งชื่อสคริปต์ของผมให้ขึ้นต้นด้วยจุลภาค ก็คงเร็วกว่านี้มาก และยังช่วยให้นึกออกก่อนเปิดไฟล์ด้วยว่าแต่ละสคริปต์สร้างไว้ทำไม

    • โดยทั่วไป ~/.local/bin/ ใช้สำหรับสคริปต์ที่ติดตั้งมา ส่วนของที่เขียนเองในเครื่องจะไว้ใน ~/bin/
  • ขอผ่าน วาง bin ส่วนตัวไว้หน้าสุดของ $PATH แล้วถ้าจะอ้างถึงโปรแกรมที่ถูกบังไว้ก็ใช้ /usr/bin หรือ /bin ก็พอ
    รายการเครื่องมือที่ปรับแต่งเองสามารถไล่ดูได้ด้วย ~/bin/[Tab]

    • ไม่เข้าใจว่าทำไมต้องคอยจำเรื่องเครื่องหมายจุลภาคตลอด ในเมื่อไม่อยากให้ยูทิลิตีระบบถูกของตัวเองบัง แต่ก็ไม่อยากใช้ชื่อเดียวกัน
      ถ้าไม่ชอบ grep ของระบบ เช่น grep ของ Solaris แล้วอยากใช้ GNU grep ที่ตัวเองชอบ ทำไมไม่แค่ปล่อยให้มันชื่อ grep ไปเลย?
  • การได้เจอไอเดียนี้เมื่อ 5 ปีก่อนช่วยจัดระเบียบคลังทริกเชลล์ของผมได้ ตอนนี้มี คำสั่งแบบจุลภาค รวมทั้ง alias และ ~/bin มากกว่า 50 รายการ ทำให้ชีวิตบนเชลล์ลื่นไหลกว่าสภาพปนเปกันในอดีตมาก

  • เคยมีการถกเถียงกันในปี 2020 ด้วย: https://news.ycombinator.com/item?id=22778988 (90 ความคิดเห็น)

 
kayws426 2024-06-24

แล้วการใช้ '_' ล่ะ?