Chỉ giữ các lệnh thành công trong lịch sử BASH


20

Đôi khi tôi hiểu sai cú pháp của một lệnh:

# mysql -d test
mysql: unknown option '-d'
# echo $?
2

Tôi thử lại và làm cho đúng:

# mysql --database test
Welcome to the MySQL monitor.
mysql >
...

Làm cách nào để ngăn lệnh đầu tiên, với mã lỗi khác 0, để vào lịch sử?

Câu trả lời:


20

Tôi không nghĩ rằng bạn thực sự muốn điều đó. Quy trình làm việc thông thường của tôi diễn ra như sau:

  • Nhập lệnh
  • Chạy nó
  • Nhận thấy nó thất bại
  • Nhấn phím UP
  • Chỉnh sửa lệnh
  • Chạy lại

Bây giờ, nếu lệnh thất bại không được lưu vào lịch sử, tôi không thể lấy lại dễ dàng để sửa và chạy lại.


3
Tôi đoán một thiết kế tốt hơn sẽ là một lịch sử phiên và một lịch sử vĩnh viễn. Cảm ơn!
Adam Matan

Lịch sử được lưu khi bạn thoát khỏi thiết bị đầu cuối. Vì vậy, trong khi bạn có thể quay lại các lệnh bạn đã nhập trong phiên cuối này, nó thực sự được lưu vào lịch sử bash khi thoát khỏi thiết bị đầu cuối.
Làm

11

Cách duy nhất tôi có thể nghĩ ra để làm điều này sẽ được sử dụng history -dtrong $PROMPT_COMMAND. Vấn đề với cách tiếp cận này hoặc bất kỳ cách tiếp cận nào là không thể biết được một lệnh đã thoát có lỗi hay đã hoàn thành thành công với mã thoát khác không.

$ grep non_existent_string from_file_that_exists
$ echo $?
1

4

Thật tốt khi có bình luận không chính xác cuối cùng để sửa nó, nhưng ngay sau đó, nó trở thành rác có thể gây nhầm lẫn.

Cách tiếp cận của tôi là hai bước: lưu trữ các lệnh thất bại khi chúng thực hiện và loại bỏ chúng sau đó.

Lưu trữ các lệnh thất bại khi chúng thực hiện:

error_handler() {
    FAILED_COMMANDS="$(history | tail -1l | cut -c -5) $FAILED_COMMANDS"
}

trap error_handler ERR

trap command signalsthực thi commandkhi một trong số đó signalsđược "nâng lên".

$(command), thực hiện commandvà nắm bắt đầu ra của nó.

Khi lệnh thất bại, đoạn mã này sẽ ghi lại số lịch sử của lệnh cuối cùng được lưu vào lịch sử và lưu trữ nó trong biến để xóa trong tương lai.

Đơn giản, nhưng hoạt động không chính xác với HISTCONTROLHISTIGNORE- khi lệnh không được lưu vào lịch sử do một trong các biến, số lịch sử của lệnh cuối cùng được lưu vào lịch sử là lệnh trước đó; vì vậy, nếu lệnh không chính xác không được lưu vào lịch sử, lệnh trước đó sẽ bị xóa.

Phiên bản phức tạp hơn một chút, hoạt động chính xác trong trường hợp đó:

debug_handler() {
    LAST_COMMAND=$BASH_COMMAND;
}

error_handler() {
    local LAST_HISTORY_ENTRY=$(history | tail -1l)

    # if last command is in history (HISTCONTROL, HISTIGNORE)...
    if [ "$LAST_COMMAND" == "$(cut -d ' ' -f 2- <<< $LAST_HISTORY_ENTRY)" ]
    then
        # ...prepend it's history number into FAILED_COMMANDS,
        # marking the command for deletion.
        FAILED_COMMANDS="$(cut -d ' ' -f 1 <<< $LAST_HISTORY_ENTRY) $FAILED_COMMANDS"
    fi
}

trap error_handler ERR
trap debug_handler DEBUG

Loại bỏ các lệnh được lưu trữ sau đó:

exit_handler() {
    for i in $(echo $FAILED_COMMANDS | tr ' ' '\n' | uniq)
    do
        history -d $i
    done
    FAILED_COMMANDS=
}

trap exit_handler EXIT

Giải trình:

Khi thoát Bash, đối với mỗi số lịch sử duy nhất sẽ xóa mục nhập lịch sử tương ứng,
sau đó xóa FAILED_COMMANDSđể không xóa các lệnh kế thừa số lịch sử khỏi các lệnh đã bị xóa.

Nếu bạn chắc chắn rằng nó FAILED_COMMANDSsẽ không bị trùng lặp, bạn có thể lặp lại đơn giản trên nó
(tức là viết for i in $FAILED_COMMANDS). Tuy nhiên, nếu bạn hy vọng nó không được sắp xếp từ lớn nhất đến nhỏ nhất (trong trường hợp này luôn luôn như vậy), hãy thay thế uniqbằng sort -rnu.

Các số trong lịch sử FAILED_COMMANDSphải là duy nhất và được sắp xếp từ lớn nhất đến nhỏ nhất, bởi vì khi bạn xóa mục nhập, các số của lệnh tiếp theo sẽ bị dịch chuyển - tức là. Khi bạn phát hành history -d 2, mục thứ 3 trở thành thứ 2, thứ 4 trở thành thứ 3, v.v.

Do đó, khi sử dụng mã này, bạn không thể gọi thủ công history -d <n>
ở nơi nnhỏ hơn hoặc bằng số lớn nhất được lưu trữFAILED_COMMANDS
và mong muốn mã hoạt động chính xác.

Đây có thể là ý tưởng tốt để treo exit_handlertại EXIT, nhưng bạn cũng có thể gọi bất cứ lúc nào trước đó.

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.