เรื่องราวการพัฒนา PowerShell ของ Jeffrey Snover
(corecursive.com)- Jeffrey Snover ผลักดัน PowerShell เพื่อทำให้ Windows เป็น ระบบปฏิบัติการเซิร์ฟเวอร์ที่จัดการได้ผ่านบรรทัดคำสั่ง และต้องก้าวข้ามวัฒนธรรม Microsoft ที่เน้น GUI รวมถึงแรงต้านภายในองค์กร
- การพอร์ตเครื่องมือ UNIX ในช่วงแรกไม่สามารถแก้ปัญหาการจัดการเซิร์ฟเวอร์ได้ เพราะโครงสร้างของ Windows พึ่งพา API เช่น Registry, Active Directory, WMI มากกว่าไฟล์
- หลังจากสร้างคำสั่งที่ใช้ WMI จำนวน 70 รายการใน WMIC แล้ว เขาเปลี่ยนทิศทางไปสู่ เอนจินสร้างคำสั่งจากเมทาดาทา เพื่อลดคอขวดด้านการทดสอบและปัญหาต้นทุน
- Monad ซึ่งเป็นต้นแบบของ PowerShell เริ่มต้นไปกับกระแส .NET และ Longhorn แต่หลังจาก Longhorn ถูกรีเซ็ตก็ถูกผลักออกจาก Windows ก่อนจะกลับมามีชีวิตอีกครั้งด้วยการสนับสนุนจากทีม Exchange และองค์กร Windows Server
- Snover ยอมรับการสูญเสียตำแหน่งและผลตอบแทนเพื่อมุ่งเน้น PowerShell 1~4 และเครื่องมือนี้ได้กลายเป็นรากฐานของการทำอัตโนมัติในการจัดการ Windows และการเปลี่ยนผ่าน Office ไปสู่คลาวด์
Windows ที่เน้น GUI กับปัญหาการจัดการดาต้าเซ็นเตอร์
- PowerShell เป็นเครื่องมือคำสั่งที่เปลี่ยนการจัดการระบบ Windows แต่ภายใน Microsoft ไม่ได้เป็นโปรเจกต์ที่ได้รับการยอมรับอย่างเป็นธรรมชาติตั้งแต่แรก
- วัฒนธรรมของ Microsoft ในเวลานั้น เน้น GUI และอินเทอร์เฟซบรรทัดคำสั่งถูกมองว่าเป็นของล้าสมัย ถึงขั้นมีเกร็ดว่า Bill Gates เคยพูดว่า
command.exeจะเป็นสิ่งสุดท้ายที่เขาได้เห็น - จุดเริ่มต้นที่ Snover เข้าร่วม Microsoft คือความตระหนักว่า OS ที่ใช้ Windows NT ต้องถูกปรับให้เหมาะกับดาต้าเซ็นเตอร์และตลาดองค์กร
- เป้าหมายคือการแข่งขันกับผู้ขาย UNIX เช่น Sun, IBM, HP
- Intel และระบบนิเวศฮาร์ดแวร์แบบเปิดมีข้อได้เปรียบด้านต้นทุน แต่ซอฟต์แวร์จัดการเซิร์ฟเวอร์ยังไม่ดีพอ
โมเดลผู้ดูแลระบบที่ลดการพึ่งพาผู้วางระบบ
- การจัดการเซิร์ฟเวอร์ Windows ต้องตั้งค่าเซิร์ฟเวอร์จำนวนมากด้วยการคลิก และการตั้งค่าที่ต้องการก็แตกต่างกันในแต่ละองค์กร จึงอาจทำให้การพึ่งพา ผู้วางระบบ สูงขึ้น
- Snover มองว่าหากต้นทุนผู้วางระบบสูงขึ้น ความได้เปรียบด้านราคาของ Windows ก็จะหายไป
- เขายกตัวอย่างโครงสร้างต้นทุนเป็น ฮาร์ดแวร์ 10, ซอฟต์แวร์ 2, ผู้วางระบบ 40
- การที่ความสัมพันธ์กับลูกค้าและคุณค่าถูกย้ายไปอยู่กับผู้วางระบบก็เป็นปัญหาเช่นกัน
- ทางเลือกคือโมเดล ผู้ดูแลระบบสายโปรแกรมเมอร์ แบบ UNIX
- เป็นวิธีแก้ปัญหาเฉพาะและทำอัตโนมัติด้วยการประกอบเครื่องมือขนาดเล็กเข้าด้วยกัน
- Windows เองก็ต้องการกลุ่มผู้ดูแลระบบมืออาชีพที่จัดการสคริปต์และอัตโนมัติได้ ไม่ใช่ “ผู้ดูแลระบบที่กดปุ่ม Next อย่างเดียว”
เหตุผลที่การพอร์ตเครื่องมือ UNIX ไม่เหมาะ
- แนวทางแก้ปัญหาในช่วงแรกคือการนำเชลล์ UNIX และเครื่องมืออย่าง AWK/GREP/SED มาลงบน Windows ผ่าน Windows Services for Unix
- แต่โครงสร้างการจัดการของ Windows แตกต่างจาก UNIX
- UNIX เป็น OS ที่เน้นไฟล์ จึงจัดการงานดูแลระบบจำนวนมากได้ด้วยการแก้ไขไฟล์และรีสตาร์ตโปรเซส
- Windows เป็น โครงสร้างที่มีฟังก์ชันอยู่หลัง API เช่น Registry, Active Directory, WMI
- AWK ไม่เข้ากับ Registry โดยตรง, SED ไม่เข้ากับ Active Directory โดยตรง, GREP ไม่เข้ากับ WMI โดยตรง
- WMI มีศักยภาพสำหรับงานดูแลระบบแต่ไม่ได้ถูกใช้อย่างแพร่หลาย และทีมของ Snover ต้องการสร้างเครื่องมือบรรทัดคำสั่งสำหรับจัดการอ็อบเจกต์ WMI
WMIC และเอนจินที่ใช้เมทาดาทา
- ในยุค Windows XP พวกเขาต้องสร้างอินเทอร์เฟซบรรทัดคำสั่งที่ใช้ WMI ภายใต้ข้อจำกัดว่ามีช่วงเวลาลงโค้ดเพียง 10 สัปดาห์
- พวกเขาใช้วิศวกรสัญญาจ้างเพื่อทำ งาน 70 รายการ แต่ก็ยังไม่เพียงพออย่างมากเมื่อเทียบกับขอบเขตที่ต้องใช้ในการจัดการ Windows Server ทั้งหมด
- ภายใน Microsoft มีโครงสร้างที่ฟีเจอร์จะไม่ถูกปล่อยออกไปหากองค์กรทดสอบไม่อนุมัติ และเมื่อจำนวนคำสั่งเพิ่มขึ้น คอขวดด้านการทดสอบก็ยิ่งใหญ่ขึ้น
- Snover ผลักดันแนวทางที่มองคำสั่งแต่ละรายการเป็น เมทาดาทา ไม่ใช่โค้ด และทดสอบเฉพาะเอนจินร่วม เหมือนความสัมพันธ์ระหว่าง HTML กับเบราว์เซอร์
- เป็นแนวทางที่ให้เอนจินสร้างคำสั่ง และแสดงการตั้งค่าของแต่ละคำสั่งเป็นเมทาดาทาในรูปแบบอย่าง XML
- เขาเขียนเมทาดาทาระหว่างวันหยุดคริสต์มาสและสร้างคำสั่งได้ 72 รายการ
- เขาเล่าว่าคำสั่งเดิม 70 รายการมีค่าใช้จ่ายประมาณ 4 ล้านดอลลาร์ ส่วนเอนจินมีค่าใช้จ่ายประมาณ 60,000 ดอลลาร์
- เมื่อเพิ่มฟีเจอร์อย่างการกรองและการจัดรูปแบบเข้าไปในเอนจิน คำสั่งทั้งหมดก็ดีขึ้นพร้อมกัน และสิ่งนี้กลายเป็นลางบอกเหตุสำคัญของสถาปัตยกรรม PowerShell
Longhorn, .NET และ Monad
- Bill Gates เห็นปัญหาที่ผู้ใช้ Windows 98 ไม่ค่อยย้ายไป XP จึงต้องการทำให้ Longhorn เป็นจุดเปลี่ยนแบบเดียวกับ Windows 95 รุ่นใหม่
- Longhorn มีกำหนดจะรวมแนวทางพัฒนาบน .NET, WPF, WCF, โมเดลสตอเรจใหม่ และอื่น ๆ
- Snover ตัดสินว่า .NET อาจเป็นเครื่องมือขยายขอบเขตการจัดการ Windows ได้
- การเขียน WMI provider ไม่ได้แรงส่งเพียงพอ แต่ Bill Gates กำลังผลักดันการยอมรับ .NET อย่างแข็งขัน
- เขามองว่าหากนำยูทิลิตีจัดการไปวางบน .NET ก็จะได้การครอบคลุมที่กว้างขึ้น
- เมื่อองค์กรอื่นพยายามพอร์ต K-shell เพื่อสร้างเชลล์ Snover อธิบายแนวทางที่ดีกว่า แต่ไม่สามารถโน้มน้าวได้
- เขาขังตัวเองอยู่ในห้องและสร้าง โปรโตไทป์ประมาณ 10,000 บรรทัด ซึ่งมีหลักการสถาปัตยกรรมหลักของ PowerShell อยู่ในนั้น
- หลังการสาธิต ทีมนั้นก็ยอมรับไอเดีย และ Snover คิดว่านี่อาจเป็นไอเดียที่ดีที่สุดของเขา
- เพื่อเข้าร่วมโปรเจกต์นี้ เขาลงจากบทบาท chief architect ของผลิตภัณฑ์และบริการขนาดหลายร้อยถึงพันคน และยอมรับการถูกลดบทบาทในทางปฏิบัติ
Monad Manifesto และการโน้มน้าวทีม
- ทีมใหม่ตั้งชื่อโปรเจกต์ว่า Monad และเพราะกำลังคนไม่พอ จึงเอาต์ซอร์สบางงานไปยังอินเดีย
- Snover เขียน Monad Manifesto เพื่อจัดแนววิสัยทัศน์และเส้นทางสู่ความสำเร็จของโปรเจกต์
- สรุปปัญหา แนวทางเดิม แนวทางใหม่ คุณค่า และความแตกต่าง
- ทำให้ชัดเจนว่าจะมอบคุณค่าอะไรให้ผู้มีส่วนได้ส่วนเสียแต่ละกลุ่ม เช่น ผู้ดูแลระบบ provider และทีมพัฒนา
- แต่ละทีมใน Microsoft มีงานต้องทำมากมาย และอยู่ในสถานการณ์ที่แม้ไม่สร้างอินเทอร์เฟซบรรทัดคำสั่งก็จะไม่ถูกไล่ออก และแม้สร้างขึ้นมาก็ไม่ได้ทำให้ได้เลื่อนตำแหน่ง
- ข้อเสนอของ Monad คือให้แต่ละทีมผลิตภัณฑ์เขียนเฉพาะโค้ดที่จัดการอ็อบเจกต์ของตัวเอง ส่วนที่เหลือ PowerShell จะเป็นผู้จัดหาให้
- การจัดรูปแบบ การเรียงลำดับ การกรอง parser การรันจากระยะไกล การยกระดับสิทธิ์ และอื่น ๆ จะถูกจัดหาโดยฝั่ง PowerShell
- แต่ละทีมเพียงต้องบอกว่าจะจัดการอ็อบเจกต์ในโดเมนของตัวเองอย่างไร
- ทีม Active Directory ลงทุนเวลาหลายสัปดาห์เพื่อสร้าง cmdlet ไม่กี่ตัว และเมื่อกลุ่มผู้ใช้ตอบรับอย่างแรง ก็ทำให้พวกเขาเดินหน้างานเพิ่มเติม
การกลับสู่ Windows หลัง Longhorn รีเซ็ต
- ใน Longhorn การผลักดันการนำ .NET มาใช้มากเกินไปทำให้ปัญหาใหญ่ขึ้น
- ตัวอย่างเช่น ไดอะล็อก
Save Asของ Notepad ปรากฏหลังผ่านไป 1 นาที 30 วินาที เพราะใช้ไดอะล็อกร่วมที่อิง .NET/WCF และ working set เพิ่มจาก 15KB เป็น 15MB - ยังมีคำบอกเล่าว่า nightly build ใช้งานไม่ได้ประมาณ 7 เดือน
- ตัวอย่างเช่น ไดอะล็อก
- องค์กร Windows รีเซ็ตโปรเจกต์และนำโค้ด .NET ออกจาก Windows ส่วน PowerShell ก็ถูกผลักออกจาก Windows เช่นกัน
- หลังจากนั้น PowerShell ถูกกดดันให้ยกเลิกซ้ำ ๆ ด้วยเหตุผลว่าเป็นทั้งอินเทอร์เฟซบรรทัดคำสั่งและอิง .NET
- Bill Gates เข้าใจคุณค่า แต่ไม่ได้ช่วยในการป้องกันประจำวัน
- ผู้รับผิดชอบ Windows Server ให้การสนับสนุนในจังหวะสำคัญ
- ทีม Exchange มีบทบาทในการกันไม่ให้ถูกยกเลิก โดยยก “ธุรกิจระดับหลายพันล้านดอลลาร์” ที่พึ่งพา PowerShell ขึ้นมา
- ข้อกำหนด WinArch สำหรับการใส่ .NET ลงใน Windows นั้นเข้มงวดมาก แต่ทีม PowerShell เตรียมพร้อมให้เป็นไปตามเงื่อนไขทั้งหมด
- เมื่อผู้รับผิดชอบ Windows ขอให้ถอนคำขอ program manager ก็เรียกร้องให้มีการปฏิเสธอย่างเป็นทางการ และผลคือมีการเปิดกระบวนการพิจารณา
- องค์กร Windows Server มีอำนาจตัดสินใจนี้ และตัดสินว่า PowerShell ตรงตามข้อกำหนด จึงได้กลับเข้าไปอยู่ใน Windows
PowerShell 1~4 และผลกระทบจริง
- PowerShell 1 เปิดตัวเป็นส่วนหนึ่งของ Windows Vista
- หลังเปิดตัว Snover ได้รับคำแนะนำให้ไปทำอย่างอื่นและคำเตือนว่าจะเสียเปรียบในเส้นทางอาชีพ แต่เขายังคงมุ่งเน้นวิสัยทัศน์เดียวกันต่อไปจนถึง PowerShell 2, 3, 4
- เขาเล่าว่าเวอร์ชัน 1 บรรลุเป้าหมายบางส่วน เวอร์ชัน 2 และ 3 เติมเต็มฟีเจอร์ต่าง ๆ และเวอร์ชัน 4 ทำให้วิสัยทัศน์เกือบสมบูรณ์
- PowerShell ทำให้ผู้ดูแลระบบ Windows เขียนสคริปต์และทำงานซับซ้อนให้เป็นอัตโนมัติได้
- เกิดกลุ่มผู้ใช้ การถามตอบออนไลน์ การแชร์สคริปต์ และการบรรยายในคอนเฟอเรนซ์
- ผู้ดูแลระบบบางคนกลายเป็นวิทยากรมืออาชีพผ่านประสบการณ์ PowerShell
- Snover ได้เป็น Distinguished Engineer หลังจากนั้นประมาณ 5 ปี และต่อมากลายเป็น Technical Fellow
- ผู้รับผิดชอบ Office กล่าวว่าหากไม่มี PowerShell การเปลี่ยนผ่าน Office ไปสู่คลาวด์คงทำได้ยาก และการเปลี่ยนผ่าน Office ไปสู่คลาวด์ก็ส่งผลต่อการเปลี่ยนผ่าน Azure ไปสู่คลาวด์ด้วย
- ก่อนหน้านี้ การ provisioning เซิร์ฟเวอร์เป็นงานคลิก จึงทำซ้ำและแก้ไขได้ยาก
- สคริปต์ทำให้สามารถขยายได้ และเมื่อเกิดปัญหาก็สามารถเปลี่ยนสคริปต์เพื่อรับมือได้
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
หากมองจากมุมของผู้ดำเนินรายการ PowerShell เผชิญแรงต่อต้านอย่างหนักภายใน Microsoft และ Jeffrey Snover ผู้สร้างมันถึงขั้นถูกลดตำแหน่งเพราะพยายามผลักดันเรื่องนี้
เดิมที Jeffrey ถูกดึงตัวเข้ามาเพื่อช่วยให้ Microsoft เรียนรู้วิธีแข่งขันในตลาดศูนย์ข้อมูล แต่ในเวลานั้นวัฒนธรรมองค์กรยังยึดติดกับโลกทัศน์ที่มีคอมพิวเตอร์ส่วนบุคคลเป็นศูนย์กลางมากเกินไป จึงถูกต่อต้านในทุกขั้นตอน
อีกประเด็นที่น่าสนใจคือ PowerShell เกิดขึ้นเพราะ Windows ไม่ได้มีพื้นฐานแบบอิงไฟล์ เป้าหมายของ Jeffrey คือการจัดการเซิร์ฟเวอร์ แต่บน Windows ไม่สามารถจัดการได้ด้วยการแก้ไขไฟล์ตั้งค่าเพียงอย่างเดียว ต้องเรียก API หลายตัวเพื่อส่งรับข้อมูลแบบมีโครงสร้าง ดังนั้นโมเดลอ็อบเจ็กต์ที่สมบูรณ์จึงแทบจะเป็นหนทางเดียว
ทรานสคริปต์นี้ทำขึ้นตามลำดับคือถอดความแบบมืออาชีพ ใช้ Descript ให้ GPT-4 จัดการเครื่องหมายวรรคตอน แล้วตรวจไล่ด้วยตนเอง ดังนั้นคุณภาพอาจไม่ได้สูงเท่าที่คาด
เหตุผลที่ชอบ PowerShell คือมันเป็นภาษาไดนามิกที่เรียบง่าย และสามารถเชื่อมคำสั่งที่ใช้ง่ายเข้าด้วยกันได้ จึงอยากให้มีชุด cmdlet ใหม่สำหรับสร้างอินเทอร์เฟซผู้ใช้และกราฟแบบง่าย ๆ
ตัวอย่างเช่นความสามารถอย่าง
Create-Chart -Type "Bar" -XAxis $Cities -YAxis $GDP -OutputFile "C:/Documents/ProjectAnalysis/CitiesBarGraph.png"น่าจะเป็นสิ่งที่ Microsoft ใส่เข้าไปในผลิตภัณฑ์ได้ไม่ยาก และช่วยตัดโค้ด boilerplate ยาวหนึ่งหน้าที่ผู้ใช้ไม่ได้เข้าใจดีนักออกไปได้น่าจะมีผู้ใช้นับล้านที่เขียนโปรแกรมพื้นฐานได้ แต่เครื่องมืออย่าง Java หรือ C# ไม่เหมาะกับงานของตน โดยทั่วไป Python มักจะเหมาะ แต่ก็อยากให้ Microsoft สร้างอะไรเพิ่มเติมที่ไม่ใช่แค่สำหรับผู้ดูแลเซิร์ฟเวอร์หรือฝ่าย IT แต่ผู้ใช้สายงานทั่วไปก็ใช้ได้ด้วย
หากลงทุนเพิ่มเพื่อไม่ให้ PowerShell ช้าในงานอย่างการพาร์สไฟล์ และเพิ่มความสามารถที่กล่าวถึงข้างต้นหรือ cmdlet สำหรับสถิติและงานวิทยาศาสตร์เข้าไปด้วย มันอาจกลายเป็นเครื่องมือที่ยอดเยี่ยมทีเดียวสำหรับ นักวิเคราะห์ธุรกิจ ทั่วไปในการสร้างซอฟต์แวร์ปรับปรุงงาน หรือทำต้นแบบส่งต่อให้ทีมพัฒนาได้อย่างรวดเร็ว
ดูเหมือน Microsoft จะมองว่ามีทางเลือกอยู่สามแบบคือ นักพัฒนาซอฟต์แวร์เต็มตัวที่ใช้ C#, งาน IT ที่ใช้ PowerShell, และ Excel สำหรับผู้ใช้ทางธุรกิจ Excel นั้นยอดเยี่ยมในหลายด้านแต่ก็ค่อนข้างจำกัด และ VBA+Excel เป็นหนึ่งในระบบนิเวศที่จำกัดที่สุดเท่าที่เคยใช้งานมา ภาษา third-party อย่าง Python, R ก็เป็นทางเลือกที่สี่อยู่แล้ว แต่ก็อยากให้ Microsoft ใช้เวลากับพื้นที่นี้มากกว่านี้
เคยชอบ PowerShell รวมถึงข้อบกพร่องแปลก ๆ ของมัน แต่ตอนนี้เลิกใช้ไปแล้ว
ระหว่างอ่านเดาว่าน่าจะสร้างโดยเครื่อง แต่บอกได้ยากว่าเพราะอะไรโดยเฉพาะ และดูเหมือนต้องมีการแก้ไขอีกเล็กน้อยเพื่อให้อ่านง่ายขึ้น
ถึงอย่างนั้นก็ยังดีกว่าไม่มีทรานสคริปต์เลยมาก
ในฐานะนักพัฒนาที่ใช้ Bash มานาน ตอนที่ PowerShell ออกมาก็คาดหวังมากจริง ๆ
คิดว่าในที่สุดบน Windows ก็จะได้ใช้เชลล์เจ๋ง ๆ สำหรับงานพัฒนา แต่หลังจากนั้นก็ยังไม่เคยซึมซับ PowerShell ได้จริงจัง และบน Windows ก็ยังใช้ Bash ที่คุ้นเคยต่อไป
อยากรู้ว่านักพัฒนาที่ชำนาญทั้งสองเชลล์เปรียบเทียบกันอย่างไร อยากรู้ว่า PowerShell ทำตามคำมั่นที่ว่าเป็นเชลล์ที่มีประสิทธิภาพและทันสมัยกว่าจริงไหม หรือแค่ใช้กันเพราะติดตั้งมาให้เป็นค่าเริ่มต้นและดีกว่า CMD
ถ้าเกินประมาณ 50 บรรทัดก็ถือเป็น code smell และผมเก็บหน้านี้ไว้เผื่อต้องโน้มน้าวใครสักคน: http://mywiki.wooledge.org/BashPitfalls
ช่วงหลังได้ลองใช้ PowerShell แล้วพบว่าการที่คำสั่งคืนค่าเป็นอ็อบเจกต์ ไม่ใช่ข้อความ ทำให้ไม่ต้องฝืนจัดการข้อความ จึงง่ายกว่ามากทั้งในฐานะภาษา scripting และภาษา command line
การมีวิธีทางการสำหรับจัดการการ parse อาร์กิวเมนต์ก็ยอดเยี่ยม ทุกอย่างเป็นหนึ่งเดียวกัน และในหน้าต่าง command line แทบจะ autocomplete ตัวเลือกทั้งหมดได้ ซึ่งเป็นระดับที่ Bash ฝันไม่ถึง ทำให้ productivity สูงมาก
อย่างไรก็ตาม การแปลงชนิดข้อมูล ก็สร้างบั๊กแบบใหม่ที่ Bash ไม่เคยมี ตอนนี้ผมชอบ PWSH มากกว่า แต่ก็ไม่ค่อยชอบทั้งคู่ในระดับหนึ่ง และกำลังรอวิวัฒนาการตามธรรมชาติขั้นถัดไปอยู่
แนวคิด object-oriented ค่อนข้างมีประโยชน์เวลาสร้าง pipeline
ตัวอย่างเช่น หากต้องการจัดกลุ่มตามขนาดไฟล์แบบ recursive ภายในโฟลเดอร์เพื่อหาผู้ต้องสงสัยว่าเป็นไฟล์ซ้ำ ก็เขียนได้แบบ
Get-ChildItem -File -Recurse | Group-Object -Property Length | Where-Object { $_.Count -gt 1 } | Sort-Object -Property Countไม่ต้องจำคาถา
fileที่เข้าใจยาก และไม่ต้อง parse เอาต์พุตที่เป็นข้อความ เราได้รับอ็อบเจกต์จริงที่มี property และการ autocomplete ด้วยแท็บสามารถมองเห็นโครงสร้างนั้นได้ถ้าต้อง parse JSON ก็จัดการได้ตรง ๆ ด้วย
Get-Content -Raw whatever.json | ConvertFrom-Jsonโดยไม่ต้องใช้jqถ้าจะเปลี่ยน XML เป็น CSV ก็ใช้ConvertFrom-XmlหรือSelect-Xmlแล้วทำงานที่ต้องการ จากนั้นใช้ConvertTo-Csvถ้า
Get-ChildItemยาวเกินไปก็ใช้gci,dir,lsได้ และถ้าWhere-Objectยาวก็ใช้whereหรือ?ได้ โดยพื้นฐานแล้วยังไม่แยกตัวพิมพ์เล็กใหญ่ด้วยเมื่อไม่กี่ปีก่อนผมต้องเขียนระบบถ่ายโอนข้อมูลที่ซับซ้อน ทำงานแบบ unattended และทนทานด้วย PowerShell ประสบการณ์นั้นดีมากจนผมเปลี่ยนเชลล์บน macOS และ Linux ทั้งหมดเป็น PWSH
สิ่งที่ดีที่สุดคือพลังของการ ส่งอ็อบเจกต์ผ่าน pipeline ในฟิลเตอร์แรกผมสามารถดึงและปรับแต่ง property บางส่วนของอ็อบเจกต์ แล้วในฟิลเตอร์ถัด ๆ ไปของ pipeline ก็ยังเข้าถึง property อื่น ๆ และ property ของอ็อบเจกต์ที่ฟิลเตอร์แรกสร้างขึ้นได้ต่อไป
ความสอดคล้องของคำสั่ง การจัดการข้อผิดพลาด และ property ของอ็อบเจกต์ก็ดีมาก
ต่อมาลักษณะงานเปลี่ยนไป muscle memory เก่า ๆ กลับมา ผมจึงเปลี่ยนเชลล์ทั้งหมดกลับเป็น Bash ตอนที่ทำงานและคิดอยู่ในพื้นที่นั้นมาก ๆ PWSH ให้ความรู้สึกเป็นธรรมชาติในฐานะเชลล์ แต่พอออกจากพื้นที่นั้นแล้ว การคิดแบบ PWSH กลับยากกว่าการกลับไปใช้ Bash
บางครั้งก็คิดถึงมัน ในพื้นที่ของเชลล์ ผมมองว่าไม่มีอะไรที่เข้าใกล้ได้เท่านี้ และอย่างน้อยก็ไม่มีทางเลือกที่ใกล้พอจะทำให้ความพยายามในการย้ายไปใช้คุ้มค่า
อย่างแรกคือการพยายามเป็นภาษา .NET ผมไม่รู้ว่าคำมั่นของ .NET ที่ว่ารันไทม์เดียวกับหลายภาษา ทำไมฝั่ง Java ถึงเบ่งบานได้แม้ไม่มีคำมั่นแบบนั้น แต่ฝั่ง .NET กลับเหี่ยวเฉา แต่ถ้าจะเขียนโค้ด .NET ผมมองว่าเขียน C# ดีกว่า
อย่างที่สองคือไม่สามารถจับ พื้นฐานของเชลล์ ให้ดีได้ รายละเอียดตอนนี้ถูกฝังไว้ในอดีตจนจำไม่ได้แล้ว แต่การจัดการ redirection พัง และสิ่งที่ใน Bash เป็นเรื่องเล็กน้อย กลับแทบเป็นไปไม่ได้ใน PowerShell ผมรู้สึกว่านักพัฒนาตื่นเต้นกับการสร้างสิ่งใหม่และทรงพลัง จนละเลยสิ่งที่ Bash และเครื่องมืออื่น ๆ ทำได้ดีอยู่แล้ว
อย่างที่สามคือความหมกมุ่นอย่างการบังคับให้ทุกชื่ออยู่ในรูป Verb-Object เรื่องนี้เป็นอัตวิสัยและคงมีคนสนับสนุน แต่ผมมองว่ามันทำให้สคริปต์ดูน่าเกลียด พิมพ์ก็ฝืนมือ และไม่ได้ช่วยให้ค้นพบหรือจดจำได้ดีขึ้นอย่างมีนัยสำคัญ
การต้องใช้ลูกศรขวาแทนแท็บก็ชวนสับสน การไม่มีคำสั่งที่คุ้นเคย และการมีกฎเกณฑ์เข้มงวดในการตั้งชื่อก็ทำให้อึดอัด
ถึงอย่างนั้น ถ้ารู้จักมันดี PowerShell ก็ดูมีความสามารถมากกว่า Bash เพราะมี ระบบชนิดข้อมูล ที่ดีกว่า และจัดการอาร์กิวเมนต์ได้ง่ายกว่า ขณะที่ค่าของ Bash แทบจะเป็นสตริงไร้รูปแบบ
https://github.com/bionicles/tree_plus/blob/main/tests/more_... เป็นเวอร์ชันที่ค่อนข้างเก่าที่ผมใช้ตอนตั้งค่าสภาพแวดล้อมทดสอบบนเครื่อง Windows และแสดงให้เห็นได้พอสมควรว่าทำอะไรได้บ้าง
ตอนที่ใช้ PowerShell เองก็ไม่ได้ถึงกับเลวร้าย แต่ไม่เข้าใจเลยว่าทำไม อาร์เรย์ที่มีความยาว 1 ถึงถูกแกะออกจากอาร์เรย์แล้วกลายเป็นชนิดข้อมูลภายใน
เพราะเรื่องนี้จึงต้องคอยระวังทุกครั้งว่าอาร์เรย์อาจมีสมาชิกเข้ามากี่ตัว และแทนที่จะจัดการแบบทั่วไปได้ ก็ต้องตรวจสอบทุกครั้งที่แก้ไข จนเกิดบั๊กมหาศาล อยากรู้ว่ามีใครรู้ไหมว่าทำไมถึงทำแบบนี้
มีฟังก์ชันหนึ่งชื่อ
WriteObjectที่เขียนค่าที่ให้มาเป็นเอาต์พุตของ cmdlet และเมื่อเรียกหนึ่งครั้ง ค่านั้นก็กลายเป็นเอาต์พุต ถ้าเรียกหลายครั้ง เชลล์ก็ไม่มีทางเลือกนอกจากรวบรวมค่าทั้งหมดนั้นเป็นอาร์เรย์แล้วทำเป็นเอาต์พุตดังนั้นถ้าการรัน cmdlet ครั้งหนึ่งเรียก
WriteObjectแค่ครั้งเดียว แต่อีกครั้งเรียกสองครั้ง ในกรณีแรกเชลล์ก็ไม่มีทางรู้ได้ว่าควรห่อเอาต์พุตเดี่ยวนั้นไว้เป็นอาร์เรย์ด้วยเช่นกัน แต่ถ้าห่อเอาต์พุตของ cmdlet เป็นอาร์เรย์เสมอ ก็จะไปรบกวน cmdlet ที่โดยความหมายแล้วมีผลลัพธ์เพียงหนึ่งเดียวอย่างGet-Dateด้วยเหตุผลบางอย่าง ดูเหมือนพวกเขาไม่อยากทำให้ API ซับซ้อนขึ้นเพื่อให้ cmdlet เองระบุได้ว่าโดยความหมายแล้วเป็นเอาต์พุตเดี่ยวหรือหลายเอาต์พุต โดยไม่ขึ้นกับจำนวนครั้งจริงที่เรียก
WriteObjectAPI แบบนี้ไม่อาจเป็นคุณสมบัติแบบสแตติกของ cmdlet ได้ เพราะเอาต์พุตอาจเปลี่ยนไปมากตามพารามิเตอร์ และเนื่องจากต้องทำงานกับอาร์เรย์ว่างได้ด้วย โอเวอร์โหลดWriteObjectแบบ(Object, bool iMightWriteMoreValues)ก็ลำบาก น่าจะต้องมีฟังก์ชันแยกอย่างIWillWriteMultipleValues()มีอธิบายไว้ที่นี่ด้วย: https://news.ycombinator.com/item?id=40874873
$Ary = @(, "value")ตอนสร้างอาร์เรย์ โดยเฉพาะอาร์เรย์ขนาดใหญ่ มันมีประสิทธิภาพดีกว่า
+=และฟีเจอร์ที่ให้กำหนดforloop ให้กับอาร์เรย์ก็ถือว่าใช้ได้ดีทีเดียว แม้จะไม่ใช่วิธีที่ตรงไปตรงมานักในการเติมข้อมูลลงอาร์เรย์ แต่ก็มีประโยชน์แน่นอน$Ary = foreach ($Obj in $Objs) { @{ foo = $Obj.foo } }นักพัฒนาซอฟต์แวร์ควรระวังแรงยั่วยวนที่จะทำให้ซอฟต์แวร์ของตัวเองฉลาดเกินไปอยู่เสมอ
อีกครึ่งที่ขาดไปน่าจะเป็นความสามารถในการแปลงค่าเดี่ยวให้เป็นอาร์เรย์ความยาว 1 โดยอัตโนมัติ เมื่อฝั่งรับคาดหวังอาร์เรย์
ทั้งที่มีอย่าง Lua อยู่แล้ว แต่แทนที่จะใช้ตามนั้นหรือปรับนิดหน่อยเพื่อให้ได้ภาษาที่เรียบง่าย เล็ก สง่างาม และสอดคล้องกัน ผู้คนกลับสร้างล้อขึ้นมาใหม่ แล้วยังทำให้มันกลมไม่ได้ด้วย เหมือนโศกนาฏกรรมของซิซิฟัส
ถ้าไม่ใช่กรณีที่ต้องโต้ตอบกับซับซิสเต็มของ Windows จนจำเป็นต้องใช้คำสั่ง PowerShell เฉพาะ ก็จะคิดว่า “ทำไมไม่ใช้ Python ล่ะ?”
สำหรับ 90% ของงานที่ทำด้วย Bash มันยืดยาวและช้าเกินไป และก็เช่นเดียวกับงานที่ในอีกชีวิตหนึ่งคงใช้ Perl ทำ
มักสงสัยว่าทำไม Microsoft ถึงไม่สร้างมันบน Python, Node อะไรทำนองนั้น จำไม่ได้ว่า PowerShell ออกมาครั้งแรกเมื่อไหร่ เลยไม่แน่ใจว่าในตอนนั้นอะไรจะเป็นตัวเลือกที่เหมาะที่สุด
อีกอย่าง มันไม่ได้ถูกออกแบบมาให้เหมาะกับงานคอนโซลที่ PowerShell ทำได้ดี เพราะเรื่องที่ควรจะแค่รับเนื้อหาไฟล์แล้วส่งต่อไปยังคำสั่งอื่น กลับต้องมาจัดการเองในทำนองเดียวกับการดูแล file handle
เหตุผลที่ PowerShell ดีคือมันเป็น มีดพับสวิส ที่มี REPL ยอดเยี่ยมพร้อมการเติมข้อความอัตโนมัติ ไม่มีพฤติกรรมช่องว่างแปลก ๆ, readline[0] และถ้าจำเป็นก็ทำทุกอย่างที่ .NET ทำได้
แถมยังเป็นเชิงวัตถุ ทำให้คุณโฟกัสกับงานจริงที่ต้องทำได้ แทนที่จะต้องหาวิธี parse เอาต์พุตแบบข้อความของยูทิลิตีเก่า ๆ ด้วยยูทิลิตีเก่า ๆ อีกตัว
0: https://learn.microsoft.com/en-us/powershell/module/psreadli...
ใน “Powershell in Action” ฉบับพิมพ์ครั้งแรกของ Bruce Payette มีคำอธิบายเสริมว่า PowerShell ดูเหมือน Perl เพราะใช้สัญลักษณ์
@, ตัวแปรพื้นฐาน$_, และตัวดำเนินการเรียกฟังก์ชัน&จริง ๆ แล้วครั้งหนึ่งเคยใช้ Perl เป็นภาษาต้นแบบ และองค์ประกอบเหล่านี้ก็มาจากช่วงนั้น ภายหลังไวยากรณ์ถูกปรับให้เข้ากับ C# มากขึ้น แต่องค์ประกอบเหล่านี้ยังคงไว้เพราะทำงานได้ดี และในศัพท์แบบ Perl เขาอธิบายว่ามันมีส่วนอย่างมากต่อ “whipupitude quotient” ของภาษานอกจากนี้ ภาษาหลักของ PowerShell อิงตามไวยากรณ์ POSIX 1003.2 ของ Korn shell และเดิมทีนำ idiom ของ Perl มาใช้กับแนวคิดขั้นสูงอย่าง hash table แต่เมื่อโครงการดำเนินไป ก็ชัดเจนขึ้นว่าการปรับไวยากรณ์ของ PowerShell ให้เข้ากับ C# นั้นเหมาะสมกว่า
เหตุผลที่ไม่ได้อิงจากอย่างอื่น อาจเป็นเพราะ Microsoft ควบคุม .NET อยู่
ไม่คิดว่าสาเหตุที่ช้าเป็นเพราะ .NET แต่น่าจะเป็นปัญหาการออกแบบหรือ การลงทุนด้านประสิทธิภาพไม่เพียงพอ มากกว่า
แต่ก็ขึ้นอยู่กับว่าคุณใช้ PS เวอร์ชันไหน จากที่จำได้ เวอร์ชันใหม่ ๆ ค่อนข้างเร็ว
ที่ทำงาน ผมได้รับ “พร” ให้ดูแลโค้ดเบส stored procedure ของ SQL Server ที่มีอายุกว่า 20 ปี
เป็นโค้ดธุรกิจแกนหลักราว 300,000 บรรทัดที่ผ่าน monkey testing มาแล้ว แต่ไม่มี source control ไม่เคยมีการปรับจูนประสิทธิภาพอย่างจริงจัง แก้ไขและรัน SQL ใน SSMS แล้ว deploy ไปยัง environment ต่าง ๆ และแน่นอนว่าไม่มี automated test
บริษัทใช้ Windows เป็นหลัก พัฒนาบน Mac และใช้ Linux ใน GitHub Actions
เราเลือกใช้เครื่องมืออย่าง PowerShell Core, sqlcmd, docker สำหรับรันอินสแตนซ์ Windows SQL Server, RedGate SQL Compare สำหรับดึง schema และโค้ดจากเซิร์ฟเวอร์ legacy เดิม, tSQLt สำหรับ unit test, TSqlLint สำหรับตรวจความสอดคล้องของโค้ด, SQLFluff สำหรับตรวจ style และ Flyway สำหรับ deploy
ไม่นานก็พบว่าเมื่อ Windows ต้องเป็นหนึ่งในแพลตฟอร์ม PowerShell Core คือ cross-platform scripting shell ที่ทำงานร่วมกับระบบอื่นได้ดีที่สุด
การเขียนโค้ดไม่ได้สนุกนัก engine ของ regex มาจาก .NET และมีปัญหา backtracking ร้ายแรง พฤติกรรมของ array ก็แปลก ๆ การเรียก executable ให้ทำงานตามที่ต้องการและจับ output stream ก็ไม่คงเส้นคงวา บ่อยครั้งต้องรัน process แล้ว redirect output ไปไฟล์ชั่วคราว จากนั้นค่อยอ่านไฟล์นั้นเมื่อ child process จบ การ pipe stdout ของ child process เข้าไปในตัวแปร string ก็ยุ่งยากเกินจำเป็น
แต่ PowerShell Core รันเร็ว ถ้ามีสิ่งหนึ่งที่ Microsoft ทำได้ดี ก็คือ micro-optimization เครื่องมือที่โต้ตอบกับผู้ใช้ก็ใช้ดี เช่นตัวเลือกแบบ ASCII art list หรือ prompt รับ input ที่สร้างได้ง่าย และถ้าหลีกเลี่ยงโฟลเดอร์ที่มีไฟล์ถูกล็อกไว้ ก็จะช่วยซ่อนความประหลาดเฉพาะตัวของ Windows file system ได้เกือบหมด
ถ้าค้นหาข้อมูลอย่างตั้งใจ โดยมากก็ทำสิ่งที่ต้องการได้ ถือว่าแนะนำได้
เห็นด้วยกับความเห็นอื่น ๆ ที่บอกว่าเมื่ออาชีพเริ่มเข้าใกล้งานดูแลระบบ Windows มากขึ้น ประสบการณ์นั้นน่ารังเกียจจริง ๆ
แต่ต่างจากทุกอย่างอื่นของ Windows ที่แข็งกระด้างสุด ๆ ตัว PowerShell เอง จริง ๆ แล้วค่อนข้างยอดเยี่ยม และให้ความรู้สึกว่าออกแบบมาอย่างรอบคอบเสมอ
Linux ยอดเยี่ยม และผมจะยังใช้เป็นสภาพแวดล้อมประจำวันสำหรับงานต่อไป แต่การใช้ Bash นั้นแย่มากจริง ๆ ถึงอย่างนั้นเพราะมันมีอยู่ทุกที่เสมอ ทุกคนจึงหยิบมันมาใช้ก่อน และเราคงยังต้องรับมือกับ Bash script ที่มีข้อบกพร่องสารพัดไปจนถึงปี 2100
PowerShell ดูเหมือนเป็นผลิตผลจาก ความมั่นใจแบบผูกขาดของ Microsoft จริง ๆ
การสร้างภาษาที่แทบไม่มีสะพานเชิงไวยากรณ์ให้คนจากภาษาอื่นย้ายเข้ามาได้นั้นเป็นเรื่องกล้าหาญมาก คำสั่ง พารามิเตอร์ และ flag เดาหรือคาดคะเนไม่ได้เลย แม้คำนึงถึงความทะเยอทะยานของ Microsoft ก็ควรจะรู้ว่ากองทัพผู้ดูแลระบบและโปรแกรมเมอร์จะต้องเรียนรู้และดูแลทั้ง PowerShell กับ Bash script ควบคู่กันไปอย่างน้อยอีกหลายสิบปี
ไวยากรณ์ที่ยืดยาวสุดขีดอาจดูดีในการนำเสนอต่อคณะกรรมการ แต่เมื่อใช้จริงบ่อย ๆ มันชนเข้ากับข้อจำกัดของสมองมนุษย์ที่มีงานวิจัยรองรับเป็นอย่างดี เมื่อขนาดข้อมูลหรือ latency เกินระดับหนึ่ง flow ของการจดจ่อจะขาด ต้องใช้สมาธิ การท่องจำแบบชัดแจ้ง และการตรวจซ้ำ ต่อให้ฝึกแล้วก็ยังยากที่จะรันคาถา shell ที่ใช้บ่อยเพื่อเปลี่ยนความคิดให้เป็นจริงได้อย่างรวดเร็ว แค่รอ autocomplete โผล่ขึ้นมาแล้วตัดสินใจว่าจะรับคำถัดไปของคำสั่งหลายส่วนหรือไม่ ก็ต้องต่อสู้กับ syntax แล้ว
ถ้าค้นหา
pow..ใน Start menu จะมีตัวเลือกสุดเท่สี่อย่างขึ้นมา คือ PowerShell, PowerShell ISE และเวอร์ชันปกติกับ x86 ของแต่ละตัว ไม่ว่าจะเลือกอันไหนก็มีช่วงโหลดจน flow สะดุด ISE แสดง splash screen เล็ก ๆ แล้วกระโดดไปอีกตำแหน่ง หน้าต่าง dialog อีกอันแจ้งว่าปิด session ก่อนหน้าโดยไม่ได้บันทึกไฟล์ script ที่ไม่มีชื่อ แต่สุดท้ายก็เปิดกลับมาให้ตามที่คาดอยู่ดี แล้วจะดุไปทำไมก็ไม่รู้คุณสามารถพิมพ์หรือคัดลอกข้อความเพื่อรัน malware อะไรก็ได้ แต่ถ้าบันทึกเป็นไฟล์แล้วรันเป็น script
.psก็จะเริ่มขั้นตอน execution policy อันน่าขันขึ้นมา อาจเป็น trauma จากชื่อเสียงด้านความปลอดภัยแย่ ๆ ของ Internet Explorer ยุคแรกและ Windows ก็ได้ถึงอย่างนั้นผมก็พยายามจะรักมัน จนวันหนึ่ง script ไปเจอชื่อไฟล์ที่มีวงเล็บเหลี่ยม และ PowerShell ก็ตีความ
[1],[2]เหล่านั้นโดยนัยเหมือน iterator อะไรสักอย่าง: https://stackoverflow.com/questions/21008180/copy-file-with-...งานหลักอย่างหนึ่งของ scripting language คือการจัดการไฟล์ แต่ชื่อไฟล์ไม่ใช่สิ่งที่คนเขียน script ควบคุมได้ และมันควรรู้จักพื้นที่ชื่อไฟล์ที่ valid บน Windows เหตุการณ์นี้ทำให้ผมมีปัญหาความเชื่อมั่นต่อภาษานั้นในระยะยาว
ทีม Azure น่าจะมีอำนาจพอภายใน Microsoft จึงสร้าง syntax ที่มีสติและอ่านง่ายขึ้นมาแยกต่างหาก เช่น
az find vm,az account showอีกด้านหนึ่งคือความต้องการความสม่ำเสมอ ความรู้เกี่ยวกับ *NIX โดยพื้นฐานแล้วมักได้มาจากการท่องจำแบบดิบ ๆ
-vโดยทั่วไปคือ verbose และ-hโดยทั่วไปคือ help ก็จริง แต่ในทางปฏิบัติพึ่งพาอะไรไม่ได้เลยเมื่อมองย้อนกลับไปตอนนี้ แปลกที่ Microsoft มองไม่เห็นคุณค่าของการทำให้การตั้งค่าทั้งหมดของ Windows และแอปพลิเคชันองค์กรสำคัญอย่าง Active Directory, Exchange สามารถประกอบรวมกันได้ง่ายและตั้งโปรแกรมได้
ความคิดที่เสนอให้ใช้ Remote Desktop เข้าไปแล้วคลิกเมาส์ไปมาเป็นทางเลือกนั้นเหลวไหลมาก การ automate งานแบบนั้น อย่างน้อยจากประสบการณ์ที่เคยใช้ AutoHotkey กับ Window Spy ก็ยากและน่ารำคาญสุด ๆ
วิธี Remote Desktop เข้าไปแล้วคลิกเมาส์สร้างชั่วโมง billable ได้มากมาย
น่าขันที่ความจริงว่ามันยากและน่ารำคาญสุด ๆ อาจเป็นเหตุผลหลักที่ทำให้มีระบบปฏิบัติการทางเลือกอยู่
เมื่อก่อน ถ้าพูดอย่างยุติธรรมก็คือราว 10 ปีก่อน ผมเคยได้รับข้อเสนออย่างจริงจังจากเจ้าหน้าที่ tech support ว่าวิธีที่ดีที่สุดในการ automate การตั้งค่าบางอย่างคือ Selenium
ใช้คอมพิวเตอร์มาตั้งแต่ปี 1982 แต่ไม่เคยเป็นผู้ใช้ Windows เลยแม้แต่ครั้งเดียวจริง ๆ
ตอนที่ Wintel เริ่มผงาดในช่วงต้นทศวรรษ 1990 ก็เฝ้าตามการเติบโตของ Linux และ 386BSD และตอนที่ Win95 กับ NT ครองเดสก์ท็อปธุรกิจในช่วงปลายทศวรรษ 1990 ก็หนีไปใช้ SPARCStation, Linux และฮาร์ดแวร์ NeXT ที่เลิกผลิตไปแล้ว หลังเปลี่ยนศตวรรษก็หันมารับ Mac OS ที่เพิ่งเป็นไปตามมาตรฐาน POSIX
เกือบครึ่งศตวรรษที่ผ่านมา แก่นของนโยบายการใช้คอมพิวเตอร์คือการหลีกเลี่ยงผลิตภัณฑ์ของ Microsoft โดยมีข้อยกเว้นที่น่าพูดถึงราว ๆ Applesoft BASIC
แต่ PowerShell นั้นดี
แต่ย่อหน้าแรกสร้างความคาดหวังไว้ แล้วจงใจหลบไปในย่อหน้าที่สอง เลยอยากให้ช่วยอธิบายว่าทำไมถึงมองว่า PowerShell “ดี”
เวลาต้องเขียนเครื่องมือ command line เองด้วยภาษาโปรแกรมจริง ๆ อย่าง C/C++ ความต่างด้าน productivity ระหว่างการทำไว้ใช้กับ shell แบบดั้งเดิมกับการทำไว้ใช้กับ PowerShell มักถูกประเมินต่ำไป
โดยทั่วไปแทบไม่เคยทำเครื่องมือ CLI ที่มีประโยชน์ได้โดยไม่ต้องมีโค้ดรก ๆ ต่ำกว่าหลายพันบรรทัด ปกติต้องจัดการทั้ง pipeline input, พารามิเตอร์แบบเลือกได้, พารามิเตอร์ที่มีค่า, ค่า default และการ override, โหมด dry run, ความต้องการรูปแบบ output หลากหลาย ฯลฯ ทำให้ 90% เป็นส่วนเกิน และมีเพียง 10% ที่เป็นการทำงานจริง
ใน PowerShell โมดูล C# โดยพื้นฐานมี overhead แค่ราว 20 บรรทัด ที่เหลือทั้งหมดคือการทำงานจริง productivity น่าทึ่งมาก
ได้ทั้งการตรวจสอบพารามิเตอร์, การ autocomplete ชื่อพารามิเตอร์ด้วย Tab, pipeline input และ output, การจัดรูปแบบ, strong typing, globbing ฯลฯ มาฟรีทั้งหมด
งานบำรุงรักษาและงานจัดการกำลังย้ายจากเครื่องมือ CLI ไปสู่ภาษา compiled หรือภาษา interpreted มากขึ้นเรื่อย ๆ