การใช้เอาต์พุตจากคำสั่งล่าสุดใน Bash


180

เอาต์พุตของคำสั่ง Bash ถูกเก็บในการลงทะเบียนหรือไม่? เช่นสิ่งที่คล้ายกับ$?การจับเอาท์พุทแทนที่จะออกจากสถานะ

ฉันสามารถกำหนดผลลัพธ์ให้กับตัวแปรด้วย:

output=$(command)

แต่มันก็พิมพ์มากกว่า ...


คำตอบ:


184

คุณสามารถใช้$(!!) เพื่อคำนวณใหม่ (ไม่ใช้ซ้ำ) เอาต์พุตของคำสั่งสุดท้าย

ตัว!!มันเองรันคำสั่งสุดท้าย

$ echo pierre
pierre
$ echo my name is $(!!)
echo my name is $(echo pierre)
my name is pierre

9
คุณสามารถช่วยตัวเองจากการพิมพ์$(your_cmd)โดยใช้ backticks: `your_cmd`ตามคู่มือ Gnu Bash พวกเขาเหมือนกันกับหน้าที่ ของหลักสูตรนี้ยังขึ้นอยู่กับคำเตือนที่ยกขึ้นโดย @memecs

ในทางปฏิบัติผลลัพธ์ของคำสั่งถูกกำหนดไว้ดังนั้นฉันจะไม่ทนทุกข์ทรมานกับการคำนวณซ้ำ มันมีประโยชน์อย่างแน่นอนเมื่อคุณต้องการทำสิ่งgit diff $(!!)ที่คำสั่งก่อนหน้าเป็นการfindเรียก
Sridhar Sarnobat

เห็นได้ชัดว่ามีเหตุผลที่ดีที่จะใช้$()แทน backticks แต่คงไม่มีอะไรจะเสียเวลานอนมากกว่า github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw

1
@ user2065875 แม้ว่า backticks $()ยังคงทำงานในทุบตีพวกเขาจะเลิกในความโปรดปรานของ
kurczynski

87

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


2
เป็นไปได้หรือไม่ที่จะมีฟังก์ชั่น 'มิดเดิลแวร์' ทุบตีกำหนดเองซึ่งทำงานก่อนทุกคำสั่งซึ่งทั้งหมดนี้ทำคือบันทึกผลลัพธ์ที่ส่งออกไปยังสิ่งที่ทำหน้าที่เป็นคลิปบอร์ดภายใน (พูด BASH_CLIPBOARD)?
Pat Needham

@ PatNeedham ไม่แน่ใจ ตรวจสอบโมดูลที่โหลดได้ ดูว่าคุณสามารถเขียนหนึ่ง
konsolebox

11

วิธีหนึ่งในการทำเช่นนั้นคือโดยใช้trap DEBUG:

f() { bash -c "$BASH_COMMAND" >& /tmp/out.log; }
trap 'f' DEBUG

ตอนนี้ stdout ของคำสั่งที่ดำเนินการล่าสุดและ stderr จะพร้อมใช้งานใน /tmp/out.log

ข้อเสียเพียงอย่างเดียวคือมันจะรันคำสั่งสองครั้ง: หนึ่งครั้งเพื่อเปลี่ยนเส้นทางเอาต์พุตและข้อผิดพลาดไปยัง/tmp/out.logและอีกครั้งตามปกติ อาจมีวิธีป้องกันพฤติกรรมนี้เช่นกัน


10
ดังนั้นเมื่อฉันพิมพ์rm -rf * && cd ..ไม่เพียงไดเรกทอรีปัจจุบัน แต่ยังผู้ปกครองหลักจะถูกลบ? วิธีการนี้ดูอันตรายมากสำหรับฉัน
phil294

1
ตอนนี้ฉันจะยกเลิกการกระทำนี้ได้อย่างไร
Kartik Chauhan

5
@KartikChauhan: แค่ทำtrap '' DEBUG
anubhava

7

หากคุณใช้ mac และไม่ต้องกังวลว่าจะเก็บเอาท์พุทของคุณไว้ในคลิปบอร์ดแทนที่จะเขียนลงในตัวแปรคุณสามารถใช้ pbcopy และ pbpaste เป็นวิธีแก้ปัญหา

ตัวอย่างเช่นแทนที่จะทำสิ่งนี้เพื่อค้นหาไฟล์และกระจายเนื้อหากับไฟล์อื่น:

$ find app -name 'one.php' 
/var/bar/app/one.php

$ diff /var/bar/app/one.php /var/bar/two.php

คุณสามารถทำได้:

$ find app -name 'one.php' | pbcopy
$ diff $(pbpaste) /var/bar/two.php

สตริง/var/bar/app/one.phpอยู่ในคลิปบอร์ดเมื่อคุณเรียกใช้คำสั่งแรก

โดยวิธีการที่PBในpbcopyและpbpasteยืนทำด้วยกระดาษแข็ง, ไวพจน์คลิปบอร์ด


1
ใน linux $ find app -name 'one.php' | xclip $ diff $(xclip -o) /var/bar/two.php
si-mikey

ที่น่าสนใจฉันไม่เคยคิดที่จะใช้pbpasteร่วมกับการทดแทนคำสั่ง
Sridhar Sarnobat

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

4

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

save_output() { 
   exec 1>&3 
   { [ -f /tmp/current ] && mv /tmp/current /tmp/last; }
   exec > >(tee /tmp/current)
}

exec 3>&1
trap save_output DEBUG

วิธีนี้เอาต์พุตของคำสั่ง last อยู่ใน / tmp / last และคำสั่งไม่ถูกเรียกสองครั้ง


1
ข้อเสียอย่างหนึ่งที่ฉันพบคือไม่มีคำสั่งของคุณคิดว่าพวกเขากำลังทำงานอยู่ใน tty อีกต่อไปซึ่งหมายความว่าคุณต้องให้ความสนใจเป็นพิเศษเพื่อให้ได้รหัสสีที่ทำงานกับระบบสาธารณูปโภคเช่นgrepหรือgit diff+1 อย่างไรก็ตาม
Marty Neal

2

อย่างที่ konsolebox บอกไว้คุณต้องแฮ็คไปทุบตีตัวเอง นี่เป็นตัวอย่างที่ดีเกี่ยวกับวิธีการที่อาจบรรลุเป้าหมายนี้ ที่เก็บ stderred (หมายถึงการระบายสี stdout) ให้คำแนะนำเกี่ยวกับวิธีการสร้าง

ฉันให้มันลอง: การกำหนดบางส่วนภายในไฟล์อธิบายใหม่.bashrcเช่น

exec 41>/tmp/my_console_log

(หมายเลขคือพล) และปรับเปลี่ยนstderred.cตามความเหมาะสมเพื่อให้เนื้อหายังได้รับการเขียนเพื่อ fd 41. มันชนิดของการทำงาน แต่มีการโหลดของ NUL ไบต์, formattings แปลกและเป็นพื้นข้อมูลไบนารีไม่สามารถอ่านได้ บางทีคนที่มีความเข้าใจที่ดีของ C สามารถลองใช้งานได้

tail -n 1 [logfile]ถ้าเป็นเช่นนั้นทุกอย่างที่จำเป็นเพื่อให้ได้เส้นที่พิมพ์สุดท้ายคือ


1

ใช่ทำไมพิมพ์บรรทัดพิเศษทุกครั้งที่ตกลงกัน คุณสามารถเปลี่ยนเส้นทางกลับไปที่อินพุต แต่เอาต์พุตไปยังอินพุต (1> & 0) นั้นไม่เป็นเช่นนั้น นอกจากนี้คุณไม่ต้องการเขียนฟังก์ชั่นซ้ำแล้วซ้ำอีกในแต่ละไฟล์สำหรับไฟล์เดียวกัน

หากคุณไม่ได้ส่งออกไฟล์ที่ใดก็ได้และตั้งใจจะใช้งานแบบโลคัลเท่านั้นคุณสามารถตั้งค่าให้เทอร์มินัลตั้งค่าการประกาศฟังก์ชัน คุณต้องเพิ่มฟังก์ชั่นใน~/.bashrcไฟล์หรือใน~/.profileไฟล์ ในกรณีที่สองคุณต้องเปิดใช้งานจากRun command as login shellEdit>Preferences>yourProfile>Command

ทำให้ฟังก์ชั่นง่ายพูดว่า:

get_prev()
{
    # option 1: create an executable with the command(s) and run it
    echo $* > /tmp/exe
    bash /tmp/exe > /tmp/out

    # option 2: if your command is single command (no-pipe, no semi-colons), still it may not run correct in some exceptions
    #echo `$*` > /tmp/out

    # return the command(s) outputs line by line
    IFS=$(echo -en "\n\b")
    arr=()
    exec 3</tmp/out
    while read -u 3 -r line
    do
        arr+=($line)
        echo $line
    done
    exec 3<&-
}

ในสคริปต์หลัก:

#run your command:
cmd="echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"
get_prev $cmd
#or simply
get_prev "echo hey ya; echo hey hi; printf `expr 10 + 10`'\n' ; printf $((10 + 20))'\n'"

Bash บันทึกตัวแปรแม้อยู่นอกขอบเขตก่อนหน้า:

#get previous command outputs in arr
for((i=0; i<${#arr[@]}; i++)); do echo ${arr[i]}; done

1

ทางออกที่ง่ายมาก

หนึ่งที่ฉันใช้มานานหลายปี

สคริปต์ (เพิ่มในของคุณ.bashrcหรือ.bash_profile)

# capture the output of a command so it can be retrieved with ret
cap () { tee /tmp/capture.out}

# return the output of the most recent command that was captured by cap
ret () { cat /tmp/capture.out }

การใช้

$ find . -name 'filename' | cap
/path/to/filename

$ ret
/path/to/filename

ฉันมักจะเพิ่ม| capในตอนท้ายของคำสั่งทั้งหมดของฉัน ด้วยวิธีนี้เมื่อฉันพบว่าฉันต้องการที่จะทำการประมวลผลข้อความบนผลลัพธ์ของคำสั่งที่ทำงานช้าฉันสามารถดึงมันด้วยresเสมอ


สิ่งที่จุดของcat -ในcap () { cat - | tee /tmp/capture.out}ที่นี่? ไม่cap () { tee /tmp/capture.out }ต้องเสียสะอึกใด ๆ ฉันหายไป?
Watson

1
@ วัตสันจุดดี! ฉันไม่รู้ว่าทำไมฉันถึงมีนิสัยแบบนั้น ฉันควรเพิ่มcat - | cat - | cat -ก่อนเพื่อให้น่าสนใจยิ่งขึ้นหรือไม่ 😉ฉันจะแก้ไขมันเมื่อฉันทดสอบเพื่อให้แน่ใจว่ามันเป็น spandrel อย่างแท้จริง
Connor

0

ไม่แน่ใจว่าสิ่งที่คุณต้องการสำหรับสิ่งนี้ดังนั้นคำตอบนี้อาจไม่เกี่ยวข้อง คุณสามารถบันทึกผลลัพธ์ของคำสั่งได้ตลอดเวลา:netstat >> output.txtแต่ฉันไม่คิดว่านั่นคือสิ่งที่คุณกำลังมองหา

มีตัวเลือกการเขียนโปรแกรมแน่นอนว่า; คุณสามารถทำให้โปรแกรมอ่านไฟล์ข้อความด้านบนหลังจากคำสั่งนั้นรันและเชื่อมโยงกับตัวแปรและใน Ruby ซึ่งเป็นภาษาที่ฉันเลือกคุณสามารถสร้างตัวแปรจากเอาต์พุตคำสั่งโดยใช้ 'backticks':

output = `ls`                       #(this is a comment) create variable out of command

if output.include? "Downloads"      #if statement to see if command includes 'Downloads' folder
print "there appears to be a folder named downloads in this directory."
else
print "there is no directory called downloads in this file."
end

ติดสิ่งนี้ในไฟล์. rb และเรียกใช้: ruby file.rbและมันจะสร้างตัวแปรออกจากคำสั่งและอนุญาตให้คุณจัดการมัน


9
"ไม่แน่ใจว่าสิ่งที่คุณต้องการสำหรับ"ดีพูดว่าคุณ (ซึ่งฉันหมายถึงฉัน ) เพียงแค่เรียกใช้สคริปต์ระยะยาวต้องการค้นหาผลลัพธ์และลืมที่จะเปลี่ยนเส้นทางก่อนที่จะดำเนินการอย่างโง่เขลา ตอนนี้ฉันต้องการพยายามแก้ไขความโง่เขลาของฉันโดยไม่ต้องเรียกใช้สคริปต์อีกครั้ง
jscs

0

ฉันมีความคิดว่าฉันไม่มีเวลาลองดำเนินการทันที

แต่ถ้าคุณทำสิ่งต่อไปนี้:

$ MY_HISTORY_FILE = `get_temp_filename`
$ MY_HISTORY_FILE=$MY_HISTORY_FILE bash -i 2>&1 | tee $MY_HISTORY_FILE
$ some_command
$ cat $MY_HISTORY_FILE
$ # ^You'll want to filter that down in practice!

อาจมีปัญหากับการบัฟเฟอร์ IO ไฟล์อาจมีขนาดใหญ่เกินไป จะต้องมีวิธีแก้ไขปัญหาเหล่านี้


0

ฉันคิดว่าการใช้คำสั่งสคริปต์อาจช่วยได้ สิ่งที่ต้องการ,

  1. สคริปต์ -c bash -qf fifo_pid

  2. การใช้คุณสมบัติทุบตีเพื่อตั้งค่าหลังจากการแยกวิเคราะห์


0

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

สามารถใช้สำหรับคำสั่งง่าย ๆ ที่คืนค่าเอาต์พุตบรรทัดเดียว (ทดสอบบน Ubuntu 18.04 ด้วยgnome-terminal)

ติดตั้งเครื่องมือดังต่อไปนี้: xdootool, xclip,ruby

ในgnome-terminalการเดินทางไปและตั้งค่าให้Preferences -> Shortcuts -> Select allCtrl+shift+a

สร้างสคริปต์ ruby ​​ต่อไปนี้:

cat >${HOME}/parse.rb <<EOF
#!/usr/bin/ruby
stdin = STDIN.read
d = stdin.split(/\n/)
e = d.reverse
f = e.drop_while { |item| item == "" }
g = f.drop_while { |item| item.start_with? "${USER}@" }
h = g[0] 
print h
EOF

ในการตั้งค่าแป้นพิมพ์ให้เพิ่มแป้นพิมพ์ลัดต่อไปนี้:

bash -c '/bin/sleep 0.3 ; xdotool key ctrl+shift+a ; xdotool key ctrl+shift+c ; ( (xclip -out | ${HOME}/parse.rb ) > /tmp/clipboard ) ; (cat /tmp/clipboard | xclip -sel clip ) ; xdotool key ctrl+shift+v '

ทางลัดด้านบน:

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