Làm thế nào tôi có thể thực thi dòng đầu ra cuối cùng trong bash?


12

Tôi biết tôi có thể chạy lệnh cuối cùng trong bash !!, nhưng làm thế nào tôi có thể chạy dòng đầu ra cuối cùng?

Tôi đang nghĩ về trường hợp sử dụng của đầu ra này:

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

Nhưng tôi không biết làm thế nào tôi có thể chạy nó. Tôi đang nghĩ về một cái gì đó giống như !!, có thể @@hoặc tương tự?


Siêu người dùng cũng có câu hỏi này.


1
Tại sao bạn không sao chép nó và dán?
Pilot6

1
@Tim Bạn muốn có cách để chạy dòng cuối cùng của đầu ra của chương trình, nhưng đối với trường hợp sử dụng cụ thể này, cũng có cách tiếp cận thay đổi command_not_found_handlehàm shell hoặc tập lệnh mà nó chạy (thường chỉ /usr/lib/command-not-found). Tôi nghĩ rằng bạn có thể làm cho nó để bạn được nhắc tương tác với tùy chọn tự động cài đặt gói hoặc (có thể tốt hơn) để tên của gói được lưu trữ ở đâu đó và có thể được cài đặt bằng cách chạy một lệnh ngắn. Điều đó đủ khác với những gì bạn đã hỏi ở đây, bạn có thể xem xét đăng một câu hỏi riêng về nó.
Eliah Kagan


2
Cũng có thể có liên quan: github.com/nvbn/thefuck (Bỏ qua tên) công cụ này tự động sửa các lệnh git / apt / etc :)
bhathiya-perera 20/07/18

Câu trả lời:


16

Lệnh $(!! |& tail -1)nên làm:

$ 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

Như bạn có thể thấy sudo apt-get install gitlệnh đang chạy.

EDIT: Phá vỡ$(!! |& tail -1)

  • $()bashmẫu thay thế lệnh

  • bashsẽ mở rộng !!đến lệnh thực thi cuối cùng

  • |&một phần là khó khăn Thông thường, pipe |sẽ lấy STDOUT của lệnh bên trái và chuyển nó dưới dạng STDIN sang lệnh bên phải |, trong trường hợp của bạn, lệnh trước đó in đầu ra của nó trên STDERR dưới dạng thông báo lỗi. Vì vậy, làm điều |đó sẽ không giúp ích gì, chúng ta cần chuyển cả STDOUT và STDERR (hoặc chỉ STDERR) cho lệnh bên phải. |&sẽ chuyển cả STDOUT và STDERR dưới dạng STDIN sang lệnh bên phải. Thay phiên, tốt hơn bạn chỉ có thể vượt qua STDERR:

    $(!! 2>&1 >/dev/null | tail -1)
  • tail -1như thường lệ sẽ in dòng cuối cùng từ đầu vào của nó. Ở đây thay vì in dòng cuối cùng, bạn có thể chính xác hơn như in dòng có chứa apt-get installlệnh:

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

1
Cách tiếp cận của bạn giả định đầu ra sẽ giống hệt lần thứ hai. Một số lệnh có thể tạo ra một đầu ra khác vào lần tới, trong trường hợp đó, rất khó để dự đoán việc thực thi dòng cuối cùng của đầu ra của nó thực sự sẽ làm gì.
kasperd

1
@kasperd Bạn nói đúng..theo khi có liên quan đến ví dụ cụ thể này, đầu ra sẽ giữ nguyên ..
heemayl

7

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

Các cơ sở tương tác lịch sử của Bash không cung cấp bất kỳ cơ chế nào để kiểm tra đầu ra của các lệnh. Shell không lưu trữ điều đó và mở rộng lịch sử đặc biệt dành cho các lệnh bạn tự chạy hoặc một phần của các lệnh đó.

Điều này khiến cho cách tiếp cận chạy lại lệnh cuối cùng và chuyển cả stdoutstderr ( |&) thành một lệnh thay thế. Câu trả lời của heemayl đạt được điều này, nhưng không thể được sử dụng trong một bí danh vì shell thực hiện mở rộng lịch sử trước khi mở rộng bí danh, chứ không phải sau đó.

Tôi cũng không thể mở rộng lịch sử để hoạt động trong hàm shell, thậm chí bằng cách cho phép nó trong hàm với set -H. Tôi nghi ngờ !!trong một chức năng sẽ không bao giờ được mở rộng và tôi không chắc nó sẽ được mở rộng thành gì, nhưng hiện tại tôi không chắc chắn chính xác tại sao nó không hoạt động.

Do đó, nếu bạn muốn thiết lập mọi thứ để bạn có thể thực hiện việc này với rất ít thao tác gõ, bạn nên sử dụng fcshell dựng sẵn thay vì mở rộng lịch sử để trích xuất lệnh cuối cùng từ lịch sử. Điều này có lợi thế bổ sung mà nó hoạt động ngay cả khi mở rộng lịch sử bị vô hiệu hóa.

Như được hiển thị trong câu trả lời của Gordon Davisson về Tạo bí danh có chứa mở rộng lịch sử bash (trên Super User ), mô phỏng . Cắm vào này cho trong lệnh heemayl của sản lượng:$(fc -ln -1)!!!! $(!! |& tail -1)

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

Điều này hoạt động như thế $(!! |& tail -1)nhưng có thể đi trong một định nghĩa bí danh:

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

Sau khi bạn chạy định nghĩa đó, hoặc đặt nó vào .bash_aliaseshoặc .bashrcbắt đầu một shell mới, bạn có thể chỉ cần gõ @@(hoặc bất cứ thứ gì bạn đặt tên bí danh) để cố gắng thực hiện dòng đầu ra cuối cùng từ lệnh cuối cùng.

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
....

Có bất kỳ tùy chọn để di chuyển này đến một kịch bản riêng biệt? Tôi đã thử điều đó và cuối cùng đã thất bại, vì fc không cho ra kết quả gì ... Vì fc theo lịch sử của phiên shell hiện tại, do đó, việc chuyển nó sang một tập lệnh riêng sẽ mở ra một phiên khác ở đó với lịch sử trống, tất nhiên ... Tôi đã thêm một vài các lệnh phía trên dòng fc trong tập lệnh và một trong số chúng đã được thực thi. Vì vậy, tôi tự hỏi liệu có bất kỳ tùy chọn để nói với kịch bản, rằng "bối cảnh" của nó là phiên bash, đã thực hiện nó. Giống như, một số đối số cho #! / Bin / bash hoặc một cái gì đó ...
Exterminator13

@ Exterminator13 Nếu bạn có nghĩa là một kịch bản riêng biệt mà bạn chạy --like ./script, không phải là bạn nguồn với .hoặc sourceđược xây dựng trong - tôi không chắc chắn. Bash thêm vào một tệp trên thoát vỏ hoặc khi bạn chạy historyvới -ahoặc -w(xem help history). Nếu có, các lệnh trong nhiều shell tương tác sẽ được xen kẽ (xuất hiện theo thứ tự bạn đã chạy chúng). Bạn có thể làm cho nó chắp thêm ngay lập tức. . Nhưng tôi nghĩ còn nhiều việc phải làm để tạo ra fckịch bản. Tôi đề nghị đăng một câu hỏi mới về điều này. Nếu bạn làm như vậy, xin vui lòng bình luận ở đây với một liên kết đến nó.
Eliah Kagan

2

Nếu bạn đang sử dụng trình giả lập thiết bị đầu cuối trong X, như gnome-terminal, konsolehoặc xterm, bạn có thể sử dụng lựa chọn X cho một cái gì đó như sao chép và dán:

Sử dụng lựa chọn chính

Chọn toàn bộ dòng để chạy với một cú nhấp chuột trái .

Sau đó dán nó vào dòng lệnh bằng cách sử dụng một nút bấm ở giữa .

Điều này sẽ làm việc trong hầu hết các trình giả lập thiết bị đầu cuối. Nó sử dụng lựa chọn chính, mà không cần chạm vào bảng tạm - chúng là riêng biệt.
Đó là lựa chọn, và sau đó kết hợp sao chép và dán trong một thao tác, về mặt kỹ thuật.


1
@Tim Điều đó đúng - Tôi sẽ nói rõ điều đó.
Volker Siegel

1

Như Eliah Kagan đã đề cập , shell không lưu trữ đầu ra lệnh. Tuy nhiên, có một cách để có được dòng đầu ra cuối cùng của lệnh cuối cùng mà không cần chạy lại lệnh nếu bạn chạy màn hình .

Đặt các dòng sau vào bạn ~/.screenrc:

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

Sau đó, bạn có thể nhấn Ctrl+ atđể chèn dòng trước đó trong lời nhắc hiện tại. Có thể làm điều này cũng tự động nhấn enter, nhưng có lẽ nên kiểm tra nó trước khi bạn chạy và chỉ cần nhấn enter bằng tay.

Làm thế nào nó hoạt động:

  • register tđăng ký một chuỗi vào một bộ đệm có tên t. Chuỗi sau đó là một chuỗi chính.
  • bind tliên kết với khóa t(tức là thực thi bằng Ctrl+ at), process tcó nghĩa là đánh giá nội dung của bộ đệm có tên t.
  • Trình tự này có nghĩa là:
    • ^a[(= Ctrla[): vào chế độ sao chép
    • kđi lên một dòng, 0đi đến đầu dòng, y$sao chép cho đến khi hết dòng, (dấu cách) hoàn thành lệnh
    • ^a](= Ctrla]): dán nội dung sao chép
    • ^a^L(= CtrlaCtrlL) làm mới màn hình (nếu không nội dung được dán sẽ không hiển thị, vì bạn không tự gõ
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.