zsh สามารถเข้าถึง stdout ของโปรแกรมที่รันล่าสุดได้หรือไม่


16

ฉันมักจะใช้findหรือlocateค้นหาเส้นทาง

(~) locate foobar.mmpz
/home/progo/lmms/projects/foobar.mmpz

ขั้นตอนต่อไปคือการเปิดหรือจัดการไฟล์ ในกรณีที่มีความสุขเช่นนี้ฉันสามารถทำสิ่งนี้ได้:

(~) ls `!!`
ls `locate foobar.mmpz`
/home/progo/lmms/projects/foobar.mmpz

แต่ไม่มีใครมีความสุขเกินไปเมื่อมีเอาต์พุตจำนวนมากซึ่งบางอันอาจไม่เป็นเส้นทางหรืออย่างอื่น นอกจากนี้คำสั่งที่อาจทำให้สิ้นเปลืองซ้ำแล้วซ้ำอีกนั้นไม่ได้สวยงามเช่นกัน

จะมีวิธีใดบ้างที่จะลอง zsh เพื่อเก็บ stdout ไว้ในอาร์เรย์เพื่อจัดการในภายหลัง? ท้ายที่สุดมันเป็นหน้าที่ของเชลล์ในการเปลี่ยนเส้นทางสตรีมไปยังผู้ใช้ ฉันคิดว่ามันสามารถเก็บ N และ N บรรทัดสุดท้ายในตัวแปรเพื่อใช้ในภายหลังได้ทันทีเช่น$?และอื่น ๆ

ตกลงดังนั้นนี่คือเย็นสวย: /unix//a/59704/5674 ตอนนี้ฉันกำลังถามเกี่ยวกับความรู้ zsh (และการย้ายรหัสไปยัง zsh) เพื่อจับภาพประเภทนี้หลังจากแต่ละบรรทัดการเรียกใช้


ไม่ใช่หน้าที่ของเชลล์ที่จะเปลี่ยนเส้นทางไปยังผู้ใช้ แอปพลิเคชั่นเขียนเอาต์พุตไปยังอุปกรณ์ปลายทางเชลล์ไม่ได้เกี่ยวข้องกับสิ่งนั้น
Stéphane Chazelas

@StephaneChazelas แต่เป็นหน้าที่ของเชลล์ในการพูดเปลี่ยนเส้นทางให้เป็นไฟล์ตามคำขอของผู้ใช้ นอกจากนี้ยังมีสูตรสำหรับ zsh ที่ stderr สีแดงเพื่อแยกมันออกจาก stdout ฉันคิดว่านั่นแสดงถึงบทบาทของเชลล์ในการสตรีม
unperson325680

ใช่คุณสามารถทำได้โดยใช้screenหรือscriptและ precmd และ preexec hooks
Stéphane Chazelas

คำตอบ:


7

ไม่มีคุณสมบัติในการจับเอาท์พุทจากหน้าจอในเครื่องจำลองเทอร์มินัลส่วนใหญ่ ฉันดูเหมือนจะจำผู้เขียนเทอร์มินัล xterm (ผู้อ้างอิง“ อ้างอิง”) ที่ระบุว่าเป็นการยากที่จะนำไปใช้ แม้ว่ามันจะเป็นไปได้ก็ตามเชลล์จะต้องติดตามตำแหน่งที่พรอมต์สุดท้ายอยู่

ดังนั้นคุณจะไม่ต้องหนีจากการเรียกใช้คำสั่งอีกครั้งเว้นแต่ว่าคุณจะใช้กลไกที่กำหนดเองโดยเฉพาะเช่นเทอร์มินัลเช่นการคัดลอกวางด้วยเมาส์ใน xterm หรือกับแป้นพิมพ์ในหน้าจอ

มันจะเป็นไปไม่ได้อย่างมากสำหรับเชลล์ที่จะจับเอาท์พุทของคำสั่งโดยอัตโนมัติเพราะมันไม่สามารถแยกความแตกต่างระหว่างคำสั่งที่มีเทอร์มินัลที่ซับซ้อนและการโต้ตอบของผู้ใช้จากคำสั่ง

คุณสามารถรันคำสั่งอีกครั้งและจับเอาท์พุทของมัน มีหลายวิธีที่จะทำแต่ละอย่าง หากต้องการรันคำสั่งอีกครั้งคุณสามารถใช้:

  • !! การทดแทนประวัติ - สะดวกที่สุดในการพิมพ์;
  • fc -e -ซึ่งสามารถใช้ในฟังก์ชั่น

ในการดักจับเอาต์พุตคุณสามารถใช้การทดแทนคำสั่งหรือฟังก์ชันดังต่อไปนี้:

K () {
  lines=("${(f@)$(cat)}")
}
!! |K

ชุดนี้linesอาร์เรย์เพื่อการส่งออกของคำสั่งที่ถูกส่งเข้าไป


ดังนั้นไม่ว่าจะเป็น ตอนนี้ฉันรู้แล้วว่ามีคำอธิบายที่ดีว่าวิธีอัตโนมัติแบบสมบูรณ์นั้นยากเกินไปอย่างที่เป็นอยู่สิ่งที่ดีที่สุดอันดับสองคือสิ่งที่Kคุณเห็น
unperson325680

3

$lastlineนี่เป็นครั้งแรกของการตัดบางสิ่งบางอย่างที่จะนำบรรทัดสุดท้ายของการส่งออกในตัวแปรที่เรียกว่า

precmd () {                                                                                                                                                                                                        
    exec 2>&- >&-
    lastline=$(tail -1 ~/.command.out) 
    sleep 0.1   # TODO: synchronize better
    exec > /dev/tty 2>&1
}

preexec() {
    exec > >(tee ~/.command.out&)
}

นี่ใช้preexechook ของ zsh เพื่อรันexecด้วยteeเพื่อเก็บสำเนาของ stdout ของคำสั่งจากนั้นใช้precmdเพื่ออ่านเอาต์พุตที่เก็บไว้และเรียกคืน stdout เป็นเพียงเทอร์มินัลสำหรับแสดงพรอมต์

แต่มันก็ยังต้องการงาน ตัวอย่างเช่นเนื่องจาก stdout ไม่ใช่เทอร์มินัลอีกต่อไปโปรแกรมเช่นvimและlessทำงานไม่ถูกต้อง

มีข้อมูลที่เกี่ยวข้องที่เป็นประโยชน์ในคำถามเหล่านี้:


ใช่บางทีวิธีอัตโนมัติ 100% จะไม่ทำงาน มีการexecโทรจำนวนมากในรหัสคำสั่งทำงานหลายครั้งหรือฉันติดอยู่ในความหมายพิเศษหรือไม่?
unperson325680

execหากไม่มีชื่อโปรแกรมเพียงตั้งค่าการเปลี่ยนเส้นทาง
มิเคล

2

คุณสามารถทำได้โดยเพียงแค่ไพพ์ผลลัพธ์ของคุณไปteeที่ซึ่งเพิ่งบันทึกผลลัพธ์ไปยังไฟล์และแสดงผลในเวลาเดียวกัน

ตัวอย่างเช่นคุณสามารถทำสิ่งนี้ซึ่งแสดงผลลัพธ์ของคุณเหมือนปกติ แต่ยังบันทึกลงในไฟล์ /tmp/it

locate foobar.mmpz | tee /tmp/it

จากนั้น cat ไฟล์นั้นและทำการ grep เพื่อเลือกสิ่งต่าง ๆ เช่น

cat /tmp/it | grep progo | grep lms

จากนั้นเพื่อใช้งานคุณสามารถทำสิ่งนี้ได้:

vi $(!!)


2

ฉันคิดวิธีแก้ปัญหานี้ครึ่งหนึ่ง:

alias -g ___='"$(eval "$(fc -ln -1)" | tail -n 1)"'

สิ่งนี้ช่วยให้คุณสามารถเขียน___ได้ทุกจุดในบรรทัดคำสั่ง คำสั่งก่อนหน้าจะถูกเรียกใช้ซ้ำและ___จะถูกแทนที่ด้วยบรรทัดสุดท้ายของเอาต์พุต ตัวอย่างการใช้งาน:

$ touch foo bar baz
$ ls -1
bar
baz
foo
$ vim ___

vim fooคำสั่งสุดท้ายจะขยายไปยัง

นี่จะมีขอบที่คมชัดบ้าง!

  • หากคุณรวม___อยู่ในคำสั่ง แต่คำสั่งก่อนหน้านี้รวมถึง a ___, เชลล์จะหยุดทำงานในสถานะแปลก ๆ ชั่วครู่หนึ่ง คุณจะได้รับจากรัฐนี้ทันทีด้วย-CtrlC

  • คุณยังไม่สามารถกดTabเพื่อขยาย___, เช่นเดียวกับคุณ!$และสิ่งปลูกสร้างอื่น ๆ

  • คำสั่งบางคำสั่งจะแสดงเอาต์พุตต่างกันเมื่อรัน“ ปกติ” และเมื่อเชื่อมต่อกับไพพ์ (เปรียบเทียบผลลัพธ์ของlsและls | cat.) หากคำสั่งถูกเรียกใช้โดย___เป็นหนึ่งในสิ่งเหล่านี้คุณอาจสิ้นสุดการรันคำสั่งที่แตกต่างจากที่คุณคาดไว้

  • และแน่นอนถ้าคุณต้องการทำบางสิ่งบางอย่างกับสายเอาท์พุทนอกเหนือจากอันสุดท้ายสิ่งนี้จะไม่ช่วยคุณ

ฉันเลือกชื่อ___เพราะมันเป็นสิ่งที่ฉันไม่เคยต้องการที่จะรวมเป็นคำในบรรทัดคำสั่งแม้จะเป็นข้อโต้แย้ง คุณสามารถเลือกชื่อที่แตกต่างกันได้ แต่ระวังอย่าเลือกสิ่งที่อาจถูกขยายออกไปโดยไม่ได้ตั้งใจ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.