ฉันจะรันบรรทัดสุดท้ายของเอาต์พุตใน bash ได้อย่างไร?


12

ฉันรู้ว่าฉันสามารถรันคำสั่งสุดท้ายใน bash ด้วย!!แต่ฉันจะรันบรรทัดสุดท้ายของเอาต์พุตได้อย่างไร

ฉันกำลังคิดถึงกรณีการใช้งานของผลลัพธ์นี้:

The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

แต่ฉันไม่รู้ว่าฉันจะวิ่งได้อย่างไร ฉันกำลังคิดถึงบางสิ่งเช่น!!อาจ@@หรือคล้ายกัน


ผู้ใช้ขั้นสูงมีคำถามนี้ด้วย


1
ทำไมคุณไม่คัดลอกและวาง
Pilot6

1
@Tim คุณต้องการวิธีเรียกใช้บรรทัดสุดท้ายของผลลัพธ์ของโปรแกรม แต่สำหรับกรณีการใช้งานเฉพาะนี้ยังมีวิธีการเปลี่ยนcommand_not_found_handleฟังก์ชั่นเชลล์หรือสคริปต์ที่ทำงาน (โดยปกติจะเป็นเพียง/usr/lib/command-not-found) ฉันคิดว่าคุณสามารถทำได้เพื่อให้คุณได้รับแจ้งโต้ตอบกับตัวเลือกในการติดตั้งแพคเกจโดยอัตโนมัติหรือ (อาจจะดีกว่า) เพื่อให้ชื่อของแพ็กเกจถูกเก็บไว้ที่ไหนสักแห่งและสามารถติดตั้งได้โดยใช้คำสั่งสั้น ๆ มันแตกต่างจากที่คุณถามที่นี่คุณอาจลองโพสต์คำถามแยกต่างหากเกี่ยวกับมัน
Eliah Kagan


2
อาจมีความเกี่ยวข้อง: github.com/nvbn/thefuck (ไม่ต้องสนใจชื่อ) เครื่องมือนี้แก้ไขคำสั่ง git / apt / etc โดยอัตโนมัติ :)
bhathiya-perera

คำตอบ:


16

คำสั่ง$(!! |& tail -1)ควรทำ:

$ git something
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

$ $(!! |& tail -1)
$(git something |& tail -1)
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following extra packages will be installed:
git-man liberror-perl

อย่างที่คุณเห็นsudo apt-get install gitคำสั่งกำลังทำงานอยู่

แก้ไข:ทำลายลง$(!! |& tail -1)

  • $()เป็นbashรูปแบบการทดแทนคำสั่ง

  • bashจะขยาย!!เป็นคำสั่งที่เรียกใช้งานครั้งสุดท้าย

  • |&ส่วนหนึ่งเป็นเรื่องยุ่งยาก โดยปกติ|ไพพ์จะใช้ STDOUT ของคำสั่งด้านซ้ายและส่งผ่านเป็น STDIN ไปยังคำสั่งทางด้านขวาของ|ในกรณีของคุณคำสั่งก่อนหน้านี้จะพิมพ์เอาต์พุตบน STDERR เป็นข้อความแสดงข้อผิดพลาด ดังนั้นการทำเช่น|นี้จะไม่ช่วยให้เราต้องผ่านทั้ง STDOUT และ STDERR (หรือเฉพาะ STDERR) ไปยังคำสั่งด้านขวา |&จะผ่าน STDOUT และ STDERR ทั้งสองเป็น STDIN ไปยังคำสั่งด้านขวา อีกวิธีหนึ่งที่ดีกว่าคุณสามารถผ่าน STDERR:

    $(!! 2>&1 >/dev/null | tail -1)
  • tail -1ตามปกติจะพิมพ์บรรทัดสุดท้ายจากอินพุต ที่นี่แทนที่จะพิมพ์บรรทัดสุดท้ายคุณสามารถแม่นยำมากขึ้นเช่นการพิมพ์บรรทัดที่มีapt-get installคำสั่ง:

    $(!! 2>&1 >/dev/null | grep 'apt-get install')

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

1
@kasperd คุณพูดถูก .. powerouh เท่าที่ตัวอย่างเฉพาะนี้เกี่ยวข้องเอาท์พุทควรจะยังคงเหมือนเดิม ..
heemayl

7

TL; DR: alias @@='$($(fc -ln -1) |& tail -1)'

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

สิ่งนี้จะทำให้วิธีการเรียกใช้คำสั่งล่าสุดอีกครั้งและไพพ์ทั้งstdoutและstderr ( |&) เป็นการทดแทนคำสั่ง คำตอบของ heemayl ทำสิ่งนี้ได้ แต่ไม่สามารถใช้ในนามแฝงได้เนื่องจากเชลล์ทำการขยายประวัติก่อนที่จะขยายนามแฝงไม่ใช่หลังจากนั้น

set -Hฉันไม่สามารถได้รับการขยายตัวของประวัติศาสตร์ที่จะทำงานในฟังก์ชั่นเปลือกอย่างใดอย่างหนึ่งได้โดยการเปิดใช้งานในฟังก์ชั่นที่มี ฉันสงสัยว่า!!ในฟังก์ชั่นจะไม่ขยายและฉันไม่แน่ใจว่ามันจะขยายไปถ้ามันเป็น แต่ตอนนี้ฉันไม่แน่ใจว่าทำไมมันไม่ได้

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

ดังแสดงในกอร์ดอน Davisson 's คำตอบที่จะสร้างนามแฝงที่มีการขยายตัวประวัติทุบตี (ในSuper User ) จำลอง$(fc -ln -1) !!เสียบนี้!!ในคำสั่ง heemayl ของ $(!! |& tail -1)อัตราผลตอบแทน:

$($(fc -ln -1) |& tail -1)

สิ่งนี้ใช้งานได้เหมือน$(!! |& tail -1)แต่สามารถอยู่ในนิยามของนามแฝง:

alias @@='$($(fc -ln -1) |& tail -1)'

หลังจากที่คุณเรียกใช้คำจำกัดความนั้นหรือใส่ไว้ใน.bash_aliasesหรือ.bashrcและเริ่มเชลล์ใหม่คุณสามารถพิมพ์@@(หรืออะไรก็ตามที่คุณตั้งชื่อนามแฝง) เพื่อพยายามรันบรรทัดสุดท้ายของเอาต์พุตจากคำสั่งสุดท้าย

ek@Io:~$ alias @@='$($(fc -ln -1) |& tail -1)'
ek@Io:~$ evolution
The program 'evolution' is currently not installed. You can install it by typing:
sudo apt-get install evolution
ek@Io:~$ @@
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  evolution-common evolution-data-server evolution-data-server-online-accounts
....

มีตัวเลือกใดบ้างที่จะย้ายสิ่งนี้ไปยังสคริปต์อื่นหรือไม่? ฉันพยายามแล้วและล้มเหลวในที่สุดเพราะ fc ไม่มีผลอะไร ... เนื่องจาก fc ติดตามประวัติของเซสชั่นเชลล์ปัจจุบันดังนั้นการย้ายมันไปยังสคริปต์ที่แยกต่างหากจะเปิดเซสชั่นอื่นที่นั่นพร้อมกับประวัติว่างเปล่าแน่นอน ... ฉันเพิ่ม คำสั่งด้านบนบรรทัด fc ในสคริปต์และหนึ่งในนั้นถูกดำเนินการ ดังนั้นฉันจึงสงสัยว่าจะมีตัวเลือกใดบ้างที่จะบอกสคริปต์ว่า "บริบท" ของมันคือเซสชัน bash ซึ่งดำเนินการหรือไม่ เช่นมีข้อโต้แย้งบางอย่างสำหรับ #! / bin / bash หรืออะไรบางอย่าง ...
Exterminator13

@ Exterminator13 ถ้าคุณหมายถึงสคริปต์แยกต่างหากที่คุณเรียกใช้ - like ./scriptไม่ใช่ว่าคุณให้มาพร้อมกับbuiltin .หรือsourcebuiltin - ฉันไม่แน่ใจ Bash ต่อท้ายไฟล์เมื่อออกจาก shell หรือเมื่อคุณรันhistoryด้วย-aหรือ-w(ดูhelp history) ถ้าเป็นเช่นนั้นคำสั่งในเชลล์แบบโต้ตอบหลาย ๆ อันจะถูกอินเตอร์เลด (ปรากฏตามลำดับที่คุณรัน) คุณสามารถทำให้ผนวกได้ทันที . แต่ฉันคิดว่ามีอีกหลายอย่างที่ต้องทำเพื่อfcทำงานในสคริปต์ ฉันขอแนะนำให้โพสต์คำถามใหม่เกี่ยวกับเรื่องนี้ ถ้าคุณทำโปรดแสดงความคิดเห็นที่นี่พร้อมลิงค์ไป
Eliah Kagan

2

หากคุณกำลังใช้ภายใต้การจำลอง terminal X เช่นgnome-terminal, konsoleหรือxtermคุณสามารถใช้การเลือก X สำหรับสิ่งที่ต้องการคัดลอกและวาง:

ใช้การเลือกหลัก

เลือกสายทั้งเพื่อให้ทำงานได้ด้วยคลิกซ้ายสาม

แล้ววางในบรรทัดคำสั่งใช้คลิกปุ่มกลาง

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


1
@Tim นั่นเป็นความจริง - ฉันจะทำให้ชัดเจน
Volker Siegel

1

ดังที่ Eliah Kagan พูดถึงเชลล์ไม่ได้เก็บเอาท์พุทคำสั่งไว้ แต่มีวิธีการรับบรรทัดสุดท้ายคำสั่งสุดท้ายของการส่งออก, โดยไม่ต้องใช้คำสั่งอีกครั้งถ้าคุณเรียกใช้หน้าจอ

ใส่บรรทัดต่อไปนี้เข้ากับคุณ~/.screenrc:

register t "^a[k0y$ ^a]^a^L"
bind t process t

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

มันทำงานอย่างไร:

  • register ttลงทะเบียนสตริงในบัฟเฟอร์ที่มีชื่อว่า สตริงต่อไปนี้เป็นลำดับสำคัญ
  • bind tผูกคีย์t(เช่นดำเนินการโดยใช้Ctrl+ at) หมายถึงการประเมินเนื้อหาของบัฟเฟอร์ที่มีชื่อprocess tt
  • ลำดับตัวเองหมายถึง:
    • ^a[(= Ctrla[): เข้าสู่โหมดคัดลอก
    • kไปหนึ่งบรรทัดขึ้น0ไปที่จุดเริ่มต้นของบรรทัดy$คัดลอกจนถึงจุดสิ้นสุดบรรทัด (เว้นวรรค) กรอกคำสั่ง
    • ^a](= Ctrla]): วางเนื้อหาที่คัดลอก
    • ^a^L(= CtrlaCtrlL) รีเฟรชหน้าจอ (ไม่เช่นนั้นเนื้อหาที่วางจะไม่ปรากฏขึ้นเพราะคุณไม่ได้พิมพ์ด้วยตัวเอง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.