ps aux ที่เขียนด้วย Bash โดยไม่ต้อง fork
(github.com/izabera)izabera/psเป็น อิมพลีเมนเทชันด้วย Bash สำหรับจำลองเอาต์พุตที่ใกล้เคียงกับps auxภายใน Bash แม้อยู่ในสถานการณ์ที่ไม่สามารถสร้างโปรเซสใหม่ได้- เงื่อนไขหลักคืออยู่บนเครื่องที่เชื่อมต่อผ่าน
sshและมี bash shell ที่เชื่อถือได้ แต่ PID อื่น ๆ ถูกใช้หมดแล้วจนไม่สามารถสร้างโปรเซสใหม่ได้ - README นำเสนอสถานการณ์นี้เป็นตัวอย่าง คำถามสัมภาษณ์ สำหรับตำแหน่งที่ต้องใช้ความรู้ด้าน Bash/Linux
- เครื่องมือนี้ถูกอธิบายว่าช่วยให้สามารถ “แสร้งทำเหมือนเข้าถึง
ps auxที่ใช้งานได้” และไม่ได้รับประกันว่าเป็นอิมพลีเมนเทชันทดแทนที่สมบูรณ์ - ประโยคที่ว่า “ทำงานได้ 100% บนทุกเครื่องและทุกสถานการณ์” ถูกใช้เป็น การรับประกันเชิงล้อเล่น อย่างชัดเจน
โปรเจกต์นี้ทำอะไร
- เป็นโปรเจกต์ที่เขียน
ps auxด้วย Bash ล้วน ๆ - ชื่อใน README คือ “
ps auxwritten entirely in bash without ever forking” - จุดเด่นหลักของโปรเจกต์คือระหว่างการทำงานจะไม่ fork เลย
สถานการณ์ที่ตั้งสมมติฐานไว้
- สถานการณ์ตัวอย่างมีดังนี้
- เชื่อมต่อเข้าเครื่องผ่าน
ssh - ผู้ใช้อยู่ใน bash shell ที่คุ้นเคย
- แต่ PID อื่น ๆ ถูกใช้หมดแล้ว จึงไม่สามารถสร้างโปรเซสใหม่ได้เลย
- เชื่อมต่อเข้าเครื่องผ่าน
- README แนะนำว่าในเงื่อนไขแบบนี้อาจต้องการฟังก์ชันที่คล้าย
ps aux
ขอบเขตที่คาดหวังได้และ caveat
- เครื่องมือนี้ถูกแนะนำสำหรับใช้เพื่อ “kinda sorta pretend” ว่ามี
ps auxที่ใช้งานได้ - ข้อความใน README ที่ว่า “ทำงานได้อย่างสมบูรณ์ในทุกสถานการณ์บนเครื่อง 100%, รับประกัน” ถูกใช้เป็น มุกตลก แบบเกินจริง
- ดังนั้นสาระสำคัญตามคำอธิบายคือ ไม่ใช่ความเข้ากันได้อย่างสมบูรณ์ แต่เป็นการจำลอง
ps auxด้วย Bash ล้วน ๆ ในสภาพแวดล้อมสุดขั้วที่ไม่สามารถสร้างโปรเซสใหม่ได้
1 ความคิดเห็น
ความเห็นจาก Hacker News
มุกที่ว่าปัญหาที่ยากที่สุดในวิทยาการคอมพิวเตอร์สุดท้ายแล้วคือ การจัดแนวให้ตรงกัน นี่โดนใจมาก
เคยเขียนฟังก์ชันจัดคอลัมน์ให้ตรงกันมาแล้วนับครั้งไม่ถ้วนในหลายภาษา และทุกครั้งก็ทรมานไปหมด ทั้งที่ในหัวมันดูเหมือนง่ายแค่ว่า “หาความยาวสูงสุดของแต่ละคอลัมน์ แล้วเติมช่องว่างจนถึงพหุคูณถัดไปของขนาดแท็บ”
ต่อให้ใช้ f-string และความสามารถเรื่อง padding ของ Python โค้ดก็มักซับซ้อนและอ่านยากอย่างรวดเร็ว แถมแค่กำลังเขียนตัวอย่างประกอบคอมเมนต์ใหม่ก็ยังต้องแก้บั๊กไปหลายจุดจนรู้สึกสยอง
เป็นงานที่พบบ่อยขนาดนี้ก็น่าจะมีไลบรารีอยู่แล้ว และพูดตรง ๆ ว่าน่าแปลกใจที่ไม่มีใน standard library
วิธีคือดึงความกว้างคอลัมน์กับชื่อคอลัมน์จาก
descriptionของ database cursor แล้วสร้างเส้นคั่นกับ format string ก่อนพิมพ์แต่ละแถว ยังไม่แน่ใจว่ามีพลาดบั๊กร้ายแรงอะไรไปหรือเปล่า แต่ก็ไม่ได้ดูเป็นปัญหาที่ยากเป็นพิเศษzip(*table)พลิกข้อมูลเป็นตามคอลัมน์เพื่อหาความยาวสูงสุดของแต่ละคอลัมน์ แล้วค่อยพิมพ์ด้วยf"{r:<{w}}"ให้จัดตรงกันผลลัพธ์ตัวอย่างก็จะเป็นตารางที่ความกว้างคอลัมน์ตรงกัน เช่น
agony | kick | pumpค่าข้อมูลอาจมีช่องว่างอยู่ข้างใน มีการ padding ด้วยช่องว่าง บางทีก็จัดไม่ตรง และบางครั้งข้อมูลก็ล้นคอลัมน์
สุดท้ายถ้าตกลงกันไปเลยว่าอย่าใช้ข้อมูลแบบจัดคอลัมน์ แต่ใช้ฟอร์แมตที่คนอ่านได้และง่ายกว่านี้ ก็น่าจะดีกับทุกฝ่าย
ถ้า SSH เข้าเครื่องไปแล้ว Bash shell ยังอยู่ แต่ PID ถูกใช้จนหมดจนสร้างโปรเซสใหม่ไม่ได้ ผมคงไล่ดูไฟล์ใน
/proc/[pid]/เพื่อหาว่าโปรเซสไหนกำลังทำให้ พื้นที่ PID หมดkillของ Bash เป็นคำสั่ง built-in ของ shell จึงไม่ต้อง fork โปรเซสใหม่แบบ/bin/killถ้าหา parent process ที่คอยสร้างลูกจนกิน PID หมดได้ ก็หยุดมันแล้วเอาระบบกลับมาอยู่ในการควบคุมได้อีกครั้ง
สคริปต์นี้ก็พาร์ส
/procเหมือนกัน และยังไม่มีทั้ง pipe หรือการแทนค่าด้วย$(...)ที่ต้องสร้าง Bash subshell ใหม่ด้วย จึงดูค่อนข้างสะอาดดีแบบนั้นจะเรียกใช้ฟังก์ชัน POSIX ที่ต้องการได้โดยไม่ต้องรันคำสั่งแยกต่างหาก และได้รับการตอบรับที่ดี
มันอาจเร็วกว่าไปไล่หา parent process แล้วฆ่าทิ้งภายใต้ข้อจำกัดมากมาย และถ้า PID หมดแล้ว อย่างอื่นก็น่าจะพังอยู่ไม่น้อยเหมือนกัน
ps(){ (cd /proc;for i in [0-9]*;do echo $i: $(tr '\0' ' ' < $i/cmdline);done); }/proc/[pid]/เพื่อหาโปรเซสที่ทำให้พื้นที่ PID หมดนั้นถูกต้อง แต่จากคอมเมนต์ในซอร์สโค้ด ผู้เขียนบอกว่าตอนแรกหวังว่าแค่/proc/*/statusก็น่าจะพอ ทว่าไม่มีค่าพวก อัตราการใช้ CPU อยู่ในนั้น[[ $cmdline ]] && exec {cmdline}>&-กับexec {cmdline}< "$dir"/cmdline || continueทำงานอย่างไรในปี 2011 เคยไปสัมภาษณ์ตำแหน่ง SRE กับบริษัทเทคขนาดค่อนข้างใหญ่ในสหรัฐ ตอนนั้นยังไม่เคยได้ยินคำว่า SRE มาก่อนเลย
บริษัทนั้นกำลังทำทางเลือกแทน MS Office แบบบนเบราว์เซอร์ และหลัง phone screening ก็ต้องคุยกับผู้สัมภาษณ์พร้อมเขียนโปรแกรมสด ๆ ภายในตัวแก้ไขเอกสารของบริษัทเอง
เพราะในแบบประเมินตนเองให้คะแนน
shell scriptingกับLinuxไว้สูง จึงได้โจทย์ให้ทำตัวแทนnetstatด้วย Bash แต่ตอนนั้นไม่รู้เลยว่าข้อมูล socket อยู่ตรงไหนใน/proc/และอยู่ในรูปแบบไหน จึงตัดสินใจอย่างรวดเร็วว่าคงทำไม่ทันเลยเสนอว่าจะทำ
psกับfuserฉบับย่อแทน และคำตอบที่พิมพ์ลงในโปรแกรมประมวลผลคำบนเบราว์เซอร์อันแสนเลวร้ายนั้นก็ถูกยอมรับจนได้ไปสัมภาษณ์ onsite ต่อตอนนี้ย้อนมาคิดก็ไม่แน่ใจว่าสถานการณ์สมมติที่เป็นแรงบันดาลใจของแบบฝึกหัดนี้อาจมีรากฐานในโลกจริงมากกว่าที่คิด
ถ้าเป็นผมก็คงเริ่มจากตรงนั้นเหมือนกัน
เคยทำ เว็บไซต์แบบอินเทอร์แอ็กทีฟ เล่น ๆ สำหรับสำรวจสถานการณ์ที่ SSH เข้าไปแล้วสร้างโปรเซสใหม่ไม่ได้: https://oops.cmdchallenge.com
"echo *"ไม่ได้แสดงทุกไฟล์ในไดเรกทอรีต้องใช้
"echo .* *"อยากดูรายการ
"View Solutions"ของด่านอื่นเพื่อดูว่ามีวิธีเข้าหาแบบอื่นอย่างไรบ้างIzabera เป็นหนึ่งในตัวเทพของ #bash@libera
ตั้งแต่สมัย freenode ก็ได้เรียนรู้อะไรเยอะมากจากคนเก่ง ๆ แบบนั้นตลอด 10 ปีที่ผ่านมา
นี่เป็น Bash ที่สะอาดดี มาก
จากประสบการณ์ โค้ด Bash ส่วนใหญ่มักเขียนได้ไม่ดีและไม่มีประสิทธิภาพ แต่นี่ดูเป็นตัวอย่างที่ดีว่าไม่จำเป็นต้องเป็นแบบนั้น
ถ้าเข้าไปอยู่ใน POSIX shell ที่เชื่อถือได้แต่ไม่มี Bash จะทำอย่างไร?
สคริปต์ Bash นี้ไม่เข้ากันกับ POSIX
สคริปต์นี้ ใช้กับ Bash 3.2 ไม่ได้ แต่ทำงานได้บน Bash 4.2
บน Bash 3.2 จะขึ้นข้อผิดพลาด
printf: '(': invalid format characterโดยสภาพแวดล้อมตัวอย่างคือbash-3.2-33.el5_11.4.0.1การใช้งานที่ดีกว่าน่าจะเป็น ใช้ดูรายชื่อโปรเซสบนระบบที่ไม่ได้ติดตั้ง procps
ก็โอเคดี
คุณยังสามารถทำ listener และ client ด้วย Bash ได้เหมือนกัน
แต่ไม่แนะนำให้ใช้จริง