Sử dụng lại đầu ra từ lệnh cuối cùng trong Bash


180

Là đầu ra của một lệnh Bash được lưu trữ trong bất kỳ thanh ghi nào? Ví dụ, một cái gì đó tương tự như $?chụp đầu ra thay vì trạng thái thoát.

Tôi có thể gán đầu ra cho một biến với:

output=$(command)

nhưng đó là gõ nhiều hơn ...


Câu trả lời:


184

Bạn có thể sử dụng $(!!) để tính toán lại (không sử dụng lại) đầu ra của lệnh cuối cùng.

Chính !!nó thực hiện lệnh cuối cùng.

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

9
Bạn có thể tự cứu mình khỏi việc gõ $(your_cmd)bằng cách sử dụng backticks: `your_cmd`Theo hướng dẫn của Gnu Bash, chúng giống hệt nhau về mặt chức năng. Tất nhiên, điều này cũng phải chịu cảnh báo của @memecs

Trong thực tế, đầu ra của các lệnh là xác định vì vậy tôi sẽ không thống nhất về việc tính toán lại. Nó chắc chắn hữu ích khi bạn muốn làm một cái gì đó giống như git diff $(!!)lệnh trước đó là một lệnh find.
Sridhar Sarnobat

Rõ ràng có những lý do tốt để sử dụng $()thay vì backticks. Nhưng có lẽ không có gì để mất ngủ hơn. github.com/koalaman/shellcheck/wiki/SC2006
Michael Crenshaw

1
@ user2065875 mặc dù backticks vẫn hoạt động ở Bash, nhưng chúng không được ủng hộ $().
Kurczynski

87

Câu trả lời là không. Bash không phân bổ bất kỳ đầu ra nào cho bất kỳ tham số hoặc bất kỳ khối nào trên bộ nhớ của nó. Ngoài ra, bạn chỉ được phép truy cập Bash bởi các hoạt động giao diện được phép của nó. Dữ liệu riêng tư của Bash không thể truy cập được trừ khi bạn hack nó.


2
Có thể có một số chức năng 'phần mềm trung gian' tùy chỉnh chạy trước mỗi lệnh, trong đó tất cả những gì nó làm là lưu kết quả đầu ra vào một cái gì đó hoạt động như một bảng tạm bên trong (giả sử, BASH_CLIPBOARD)?
Pat Needham

@PatNeedham Không chắc chắn. Kiểm tra các mô-đun có thể tải. Xem nếu bạn có thể viết một.
konsolebox

11

Một cách để làm điều đó là bằng cách sử dụng trap DEBUG:

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

Bây giờ thiết bị xuất chuẩn và lệnh stderr được thực thi gần đây nhất sẽ có sẵn trong /tmp/out.log

Nhược điểm duy nhất là nó sẽ thực thi một lệnh hai lần: một lần để chuyển hướng đầu ra và lỗi đến /tmp/out.logvà một lần bình thường. Có lẽ có một số cách để ngăn chặn hành vi này là tốt.


10
Vì vậy, khi tôi gõ rm -rf * && cd .., không chỉ thư mục hiện tại, mà cả thư mục mẹ sẽ bị xóa ?? Cách tiếp cận này có vẻ rất nguy hiểm đối với tôi
phil294

1
Bây giờ làm thế nào để tôi hoàn tác hành động này?
Kartik Chauhan

5
@KartikChauhan: Cứ làmtrap '' DEBUG
anubhava

7

Nếu bạn đang sử dụng mac và không ngại lưu trữ đầu ra của mình trong bảng tạm thay vì ghi vào một biến, bạn có thể sử dụng pbcopy và pbpaste như một cách giải quyết.

Ví dụ: thay vì làm điều này để tìm một tệp và tìm nội dung của nó với một tệp khác:

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

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

Bạn có thể làm điều này:

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

Chuỗi /var/bar/app/one.phpnằm trong clipboard khi bạn chạy lệnh đầu tiên.

Nhân tiện, pb trongpbcopy và viết pbpastetắt của pasteboard, một từ đồng nghĩa với clipboard.


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

Thật thú vị, tôi chưa bao giờ nghĩ đến việc sử dụng pbpastecùng với thay thế lệnh.
Sridhar Sarnobat

Điều gì sẽ xảy ra nếu tôi thực hiện lần đầu tiên và sau đó nhận ra nó không hiệu quả, làm cách nào để tiết kiệm thời gian tính toán mà không chạy lại với tùy chọn thứ hai?
NelsonGon

4

Lấy cảm hứng từ câu trả lời của anubhava, điều mà tôi nghĩ là không thực sự chấp nhận được vì nó chạy mỗi lệnh hai lần.

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

exec 3>&1
trap save_output DEBUG

Bằng cách này, đầu ra của lệnh cuối cùng nằm trong / tmp / last và lệnh không được gọi hai lần.


1
Một nhược điểm mà tôi nhận thấy là không có lệnh nào của bạn nghĩ rằng chúng đang chạy trong tty nữa, điều đó có nghĩa là bạn cần đặc biệt chú ý để có được mã màu hoạt động cho các tiện ích như grephoặc git diffdù sao đi nữa
Marty Neal

2

Giống như konsolebox đã nói, bạn sẽ phải tự hack vào bash. Đây là một ví dụ khá hay về cách người ta có thể đạt được điều này. Kho lưu trữ stderred (thực sự có nghĩa là để tô màu thiết bị xuất chuẩn) đưa ra hướng dẫn về cách xây dựng nó.

Tôi đã thử: Xác định một số mô tả tệp mới bên trong .bashrcnhư

exec 41>/tmp/my_console_log

(số là tùy ý) và sửa đổi cho stderred.cphù hợp để nội dung cũng được ghi vào fd 41. Nó thuộc loại làm việc, nhưng chứa vô số NUL byte, formattings lạ và về cơ bản là dữ liệu nhị phân, không thể đọc được. Có lẽ ai đó hiểu rõ về C có thể thử nó.

Nếu vậy, tất cả mọi thứ cần thiết để có được dòng in cuối cùng là tail -n 1 [logfile].


1

Vâng, tại sao gõ thêm dòng mỗi lần đồng ý. Bạn có thể chuyển hướng được trả về đầu vào, nhưng đầu ra thành đầu vào (1> & 0) là không. Ngoài ra, bạn sẽ không muốn viết một hàm nhiều lần trong mỗi tệp cho cùng một tệp.

Nếu bạn không xuất các tệp ở bất cứ đâu và chỉ có ý định sử dụng nó cục bộ, bạn có thể thiết lập Terminal khai báo hàm. Bạn phải thêm chức năng trong ~/.bashrctập tin hoặc trong ~/.profiletập tin. Trong trường hợp thứ hai, bạn cần kích hoạt Run command as login shelltừ Edit>Preferences>yourProfile>Command.

Thực hiện một chức năng đơn giản, nói:

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<&-
}

Trong kịch bản chính:

#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 lưu biến ngay cả ngoài phạm vi trước đó:

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

1

Giải pháp rất đơn giản

Một cái mà tôi đã sử dụng trong nhiều năm.

Script (thêm vào .bashrchoặc .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 }

Sử dụng

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

$ ret
/path/to/filename

Tôi có xu hướng thêm | capvào cuối tất cả các lệnh của tôi. Bằng cách này khi tôi thấy tôi muốn xử lý văn bản trên đầu ra của lệnh chạy chậm, tôi luôn có thể truy xuất nó res.


Điểm cat -cap () { cat - | tee /tmp/capture.out}đây là gì? Có cap () { tee /tmp/capture.out }phải tôi bị mất nấc không?
Watson

1
@Watson điểm tốt! Tôi không biết tại sao tôi lại có thói quen đó. Tôi có nên thêm cat - | cat - | cat -trước chỉ để làm cho nó thú vị hơn? Tôi sẽ sửa nó một khi tôi đã kiểm tra để đảm bảo rằng đó thực sự là một kẻ phá hoại
Connor

0

Không chắc chắn chính xác những gì bạn cần điều này cho, vì vậy câu trả lời này có thể không liên quan. Bạn luôn có thể lưu đầu ra của lệnh:netstat >> output.txt nhưng tôi không nghĩ đó là thứ bạn đang tìm kiếm.

Có các tùy chọn lập trình mặc dù; bạn chỉ cần có một chương trình để đọc tệp văn bản ở trên sau khi lệnh đó được chạy và liên kết nó với một biến và trong Ruby, ngôn ngữ tôi chọn, bạn có thể tạo một biến ra khỏi đầu ra lệnh bằng cách sử dụng '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

Dán tệp này trong tệp .rb và chạy nó: ruby file.rbvà nó sẽ tạo ra một biến ra khỏi lệnh và cho phép bạn thao tác với nó.


9
"Không chắc chắn chính xác những gì bạn cần điều này cho" Chà, giả sử bạn (ý tôi là tôi ) vừa chạy một đoạn script dài, muốn tìm kiếm đầu ra, và ngu ngốc quên chuyển hướng đầu ra trước khi thực hiện. Bây giờ tôi muốn cố gắng khắc phục sự ngu ngốc của mình mà không chạy lại tập lệnh.
jscs

0

Tôi có một ý tưởng rằng tôi không có thời gian để cố gắng thực hiện ngay lập tức.

Nhưng nếu bạn làm một cái gì đó như sau:

$ 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!

Có thể có vấn đề với bộ đệm IO. Ngoài ra các tập tin có thể nhận được quá lớn. Người ta sẽ phải đưa ra một giải pháp cho những vấn đề này.


0

Tôi nghĩ rằng sử dụng lệnh script có thể giúp đỡ. Cái gì đó như,

  1. tập lệnh -c bash -qf fifo_pid

  2. Sử dụng các tính năng bash để thiết lập sau khi phân tích cú pháp.


0

Nếu bạn không muốn tính toán lại lệnh trước đó, bạn có thể tạo một macro quét bộ đệm đầu cuối hiện tại, cố gắng đoán đầu ra -supposed- của lệnh cuối cùng, sao chép nó vào bảng tạm và cuối cùng nhập nó vào thiết bị đầu cuối.

Nó có thể được sử dụng cho các lệnh đơn giản trả về một dòng đầu ra duy nhất (được thử nghiệm trên Ubuntu 18.04 với gnome-terminal).

Cài đặt các công cụ sau: xdootool, xclip,ruby

Đi gnome-terminalđến Preferences -> Shortcuts -> Select allvà đặt nó vào Ctrl+shift+a.

Tạo tập lệnh ruby ​​sau:

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

Trong cài đặt bàn phím, thêm phím tắt sau:

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 '

Phím tắt trên:

  • sao chép bộ đệm đầu cuối hiện tại vào clipboard
  • trích xuất đầu ra của lệnh cuối cùng (chỉ một dòng)
  • gõ nó vào thiết bị đầu cuối hiện tại
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.