Python shlex.join: แสดงคำสั่ง shell ให้คนอ่านโดย quote argument อย่างถูกขอบเขต
เวลา log คำสั่งจาก subprocess เรามักทำ ' '.join(argv) แล้วได้ข้อความที่ copy ไปรันไม่ได้เมื่อ argument มีช่องว่าง อัญประกาศ หรือ $ shlex.join() ซึ่งมีตั้งแต่ Python 3.8 จะ quote รายการ argument ให้เป็นข้อความสำหรับ Unix shell และออกแบบให้ย้อนกลับด้วย shlex.split() ได้
from __future__ import annotations
import shlex
import unittest
def command_for_log(argv: list[str]) -> str:
return shlex.join(argv)
class ShlexJoinTests(unittest.TestCase):
def test_round_trip_special_arguments(self):
argv = ["deploy", "--message", "ship safely", "$NOT_EXPANDED", "it's-ready"]
rendered = command_for_log(argv)
self.assertEqual(shlex.split(rendered), argv)
def test_empty_argument_is_preserved(self):
argv = ["tool", ""]
self.assertEqual(shlex.split(shlex.join(argv)), argv)
if __name__ == "__main__":
unittest.main()
ขอบเขตด้านความปลอดภัย
ใช้ shlex.join() เพื่อ แสดงผล command ที่มีอยู่แล้ว แต่ตอน execute ให้ส่ง list เข้า subprocess.run(argv, shell=False) ตามเดิม การเปลี่ยนกลับเป็น string แล้วเปิด shell=True เพิ่ม shell interpretation โดยไม่จำเป็น
เอกสารเตือนว่า quoting นี้ออกแบบสำหรับ Unix shells ไม่รับประกันกับ Windows shell หรือ shell ที่ไม่รองรับ POSIX ดังนั้น log สำหรับ PowerShell/cmd ควรใช้ renderer ที่ตรง platform และไม่ควรนำ string นี้ไปเป็น protocol ข้ามระบบ หาก log อาจมี token หรือ password ต้อง redact ที่ระดับ argument ก่อน join เพราะการ quote ไม่ได้ปกปิดความลับ
Recap
- เก็บ argv เป็น list จนถึงจุด execute
- ใช้
shlex.join()สำหรับข้อความที่คนอ่านหรือ copy - อย่าเปิด
shell=Trueเพียงเพราะมี string ที่ quote แล้ว - redact secret ก่อน render และแยก renderer สำหรับ Windows