เก็บคำสั่งที่ประสบความสำเร็จเท่านั้นในประวัติ BASH


20

บางครั้งฉันเข้าใจผิดไวยากรณ์ของคำสั่ง:

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

ฉันลองอีกครั้งและทำให้ถูกต้อง:

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

ฉันจะป้องกันคำสั่งแรกที่มีรหัสข้อผิดพลาดแตกต่างจาก 0 เพื่อป้อนประวัติได้อย่างไร

คำตอบ:


20

ฉันไม่คิดว่าคุณต้องการสิ่งนั้นจริงๆ เวิร์กโฟลว์ตามปกติของฉันมีลักษณะดังนี้:

  • พิมพ์คำสั่ง
  • เรียกใช้
  • สังเกตว่ามันล้มเหลว
  • กดปุ่ม UP
  • แก้ไขคำสั่ง
  • เรียกใช้อีกครั้ง

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


3
ฉันเดาว่าการออกแบบที่ดีกว่านั้นจะเป็นประวัติเซสชันและประวัติถาวร ขอบคุณ!
Adam Matan

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

11

วิธีเดียวที่ฉันจะคิดว่าการทำเช่นนี้จะใช้ในhistory -d $PROMPT_COMMANDปัญหาเกี่ยวกับวิธีนี้หรือวิธีการใด ๆ ก็คือเป็นไปไม่ได้ที่จะบอกได้ว่าคำสั่งออกจากข้อผิดพลาดหรือเสร็จสมบูรณ์ด้วยรหัสออกที่ไม่ใช่ศูนย์

$ grep non_existent_string from_file_that_exists
$ echo $?
1

4

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

วิธีการของฉันคือสองขั้นตอน: คำสั่งการจัดเก็บที่ล้มเหลวเมื่อพวกเขาทำและลบออกในภายหลัง

จัดเก็บคำสั่งที่ล้มเหลวเมื่อทำ:

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

trap error_handler ERR

trap command signalsดำเนินการcommandเมื่อหนึ่งในsignalsนั้น "ยก"

$(command)ดำเนินการcommandและจับเอาท์พุทของมัน

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

เรียบง่าย แต่ทำงานอย่างไม่ถูกต้องด้วยHISTCONTROLและHISTIGNORE- เมื่อคำสั่งไม่ถูกบันทึกลงในประวัติเนื่องจากหนึ่งในตัวแปรหมายเลขประวัติของคำสั่งสุดท้ายที่บันทึกไว้ในประวัติเป็นหนึ่งในคำสั่งก่อนหน้า ดังนั้นหากคำสั่งที่ไม่ถูกต้องไม่ถูกบันทึกลงในประวัติคำสั่งก่อนหน้านี้จะถูกลบ

รุ่นที่ซับซ้อนกว่าเล็กน้อยซึ่งทำงานได้อย่างถูกต้องในกรณีนั้น:

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

ลบคำสั่งที่เก็บไว้ในภายหลัง:

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

trap exit_handler EXIT

คำอธิบาย:

เมื่อออกจาก Bash สำหรับแต่ละหมายเลขประวัติที่ไม่ซ้ำกันให้ลบรายการประวัติที่เกี่ยวข้อง
จากนั้นล้างFAILED_COMMANDSเพื่อไม่ลบคำสั่งที่รับหมายเลขประวัติที่สืบทอดมาจากคำสั่งที่ถูกลบไปแล้ว

หากคุณแน่ใจว่าFAILED_COMMANDSจะไม่ซ้ำซ้อนคุณสามารถทำซ้ำได้ง่าย
(เช่นเขียนfor i in $FAILED_COMMANDS) แต่ถ้าคุณคาดหวังที่จะไม่ได้เรียงลำดับจากมากที่สุดไปหาน้อยที่สุด (ในกรณีนี้ก็มักจะเป็น) แทนที่ด้วยuniqsort -rnu

หมายเลขประวัติFAILED_COMMANDSจะต้องไม่ซ้ำกันและเรียงลำดับจากมากที่สุดไปหาน้อยที่สุดเพราะเมื่อคุณลบรายการตัวเลขคำสั่งถัดไปจะเปลี่ยน - นั่นคือ เมื่อคุณออกhistory -d 2รายการที่ 3 กลายเป็นที่ 2 ที่ 4 กลายเป็นที่ 3 ฯลฯ

ด้วยเหตุนี้เมื่อใช้รหัสนี้คุณจึงไม่สามารถโทรด้วยตนเองในhistory -d <n>
กรณีที่nมีขนาดเล็กกว่าหรือเท่ากับจำนวนมากที่สุดที่จัดเก็บไว้FAILED_COMMANDS
และคาดว่ารหัสจะทำงานอย่างถูกต้อง

มันเป็นความคิดที่ดีอาจจะขอexit_handlerที่EXITแต่คุณยังสามารถโทรได้ตลอดเวลาก่อนหน้านี้

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