จะลบและแทนที่บรรทัดสุดท้ายในเทอร์มินัลโดยใช้ bash ได้อย่างไร?


102

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

ผลลัพธ์สุดท้ายควรมีลักษณะดังนี้:

$ Elapsed time 5 seconds

หลังจากนั้น 10 วินาทีฉันต้องการแทนที่ประโยคนี้ (ในตำแหน่งเดียวกันในหน้าจอ) โดย:

$ Elapsed time 15 seconds

คำตอบ:


117

สะท้อนการกลับรถด้วย \ r

seq 1 1000000 | while read i; do echo -en "\r$i"; done

จากเสียงสะท้อนของมนุษย์:

-n     do not output the trailing newline
-e     enable interpretation of backslash escapes

\r     carriage return

19
for i in {1..100000}; do echo -en "\r$i"; doneเพื่อหลีกเลี่ยงการโทร seq :-)
Douglas Leeder

นี่คือสิ่งที่ฉันต้องการจริงๆขอบคุณ !! และการใช้คำสั่ง seq จะหยุด termianl :-)
ดีบักเกอร์

11
เมื่อคุณใช้สิ่งต่างๆเช่น "for i in $ (... )" หรือ "for i in {1..N}" แสดงว่าคุณกำลังสร้างองค์ประกอบทั้งหมดก่อนที่จะทำซ้ำนั่นจะไม่มีประสิทธิภาพมากสำหรับอินพุตขนาดใหญ่ คุณสามารถใช้ประโยชน์จากไพพ์: "seq 1 100000 | while read i; do ... " หรือใช้ bash c-style for loop: "for ((i = 0 ;; i ++)); do ... "
tokland

ขอบคุณ Douglas และ tokland - แม้ว่าการผลิตตามลำดับไม่ได้เป็นส่วนหนึ่งของคำถามโดยตรงฉันได้เปลี่ยนไปใช้ท่อที่มีประสิทธิภาพมากขึ้นของ tokland
Ken

1
เครื่องพิมพ์เมทริกซ์ของฉันเลอะกระดาษอย่างสิ้นเชิง มันยังคงมีจุดติดอยู่บนกระดาษแผ่นเดิมซึ่งไม่มีแล้วโปรแกรมนี้ทำงานนานแค่ไหน?
Rob

188

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

$ echo -e "abcdefghijklmnopqrstuvwxyz\r0123456789"
0123456789klmnopqrstuvwxyz

หากต้องการล้างบรรทัดสำหรับข้อความใหม่คุณสามารถเพิ่ม\033[Kหลังจาก\r:

$ echo -e "abcdefghijklmnopqrstuvwxyz\r\033[K0123456789"
0123456789

http://en.wikipedia.org/wiki/ANSI_escape_code


3
สิ่งนี้ใช้ได้ดีในสภาพแวดล้อมของฉัน มีความรู้เกี่ยวกับความเข้ากันได้หรือไม่?
Alexander Olsson

21
ใน Bash อย่างน้อยก็สามารถย่อเป็น\e[Kแทน\033[K.
Dave James Miller

ตอบโจทย์มาก! ทำงานได้อย่างสมบูรณ์แบบโดยใช้พุทจากทับทิม ตอนนี้เป็นส่วนหนึ่งของชุดเครื่องมือของฉัน
phatmann

@ นักพัฒนาพิกเซล: ขอบคุณที่พยายามปรับปรุง แต่การแก้ไขของคุณไม่ถูกต้อง การส่งคืนจะต้องเกิดขึ้นก่อนเพื่อเลื่อนเคอร์เซอร์ไปที่จุดเริ่มต้นของบรรทัดจากนั้น kill จะล้างทุกอย่างตั้งแต่ตำแหน่งเคอร์เซอร์นั้นไปจนถึงจุดสิ้นสุดโดยปล่อยให้ทั้งบรรทัดว่างไว้ซึ่งเป็นเจตนา คุณใส่คำเหล่านั้นไว้ที่จุดเริ่มต้นของแต่ละบรรทัดใหม่เช่นเดียวกับคำตอบของ Ken เพื่อให้เขียนเป็นบรรทัดว่าง
Derek Veit

1
สำหรับการบันทึกเราสามารถทำ\033[Gio \rก่อนหน้านี้\033[Kได้เช่นกัน\rนี้ง่ายกว่ามาก ล่องหนเช่นกัน-island.net/xterm/ctlseqs/ctlseqs.htmlให้รายละเอียดมากกว่า Wikipedia และมาจากผู้พัฒนา xterm
jamadagni

20

คำตอบของ Derek Veit ใช้งานได้ดีตราบเท่าที่ความยาวของสายไม่เกินความกว้างของขั้ว หากไม่เป็นเช่นนั้นรหัสต่อไปนี้จะป้องกันการส่งออกขยะ:

ก่อนที่จะเขียนบรรทัดเป็นครั้งแรกให้ทำ

tput sc

ซึ่งจะบันทึกตำแหน่งเคอร์เซอร์ปัจจุบัน ตอนนี้เมื่อใดก็ตามที่คุณต้องการพิมพ์บรรทัดให้ใช้

tput rc
tput ed
echo "your stuff here"

เพื่อกลับไปที่ตำแหน่งเคอร์เซอร์ที่บันทึกไว้ก่อนจากนั้นล้างหน้าจอจากเคอร์เซอร์ไปที่ด้านล่างและสุดท้ายเขียนผลลัพธ์


Wierd สิ่งนี้ไม่ได้ทำอะไรเลยใน Terminator คุณทราบหรือไม่ว่ามีข้อ จำกัด ด้านความเข้ากันได้?
Jamie Pate

1
หมายเหตุสำหรับ Cygwin: คุณต้องติดตั้งแพ็คเกจ "ncurses" เพื่อใช้ "tput"
Jack Miller

อืม ... ดูเหมือนจะไม่ทำงานถ้ามีมากกว่าหนึ่งบรรทัดในเอาต์พุต ไม่ได้ผล:tput sc # save cursor echo '' > sessions.log.json while [ 1 ]; do curl 'http://localhost/jolokia/read/*:type=Manager,*/activeSessions,maxActiveSessions' >> sessions.log.json echo '' >> sessions.log.json cat sessions.log.json | jq '.' tput rc;tput el # rc = restore cursor, el = erase to end of line sleep 1 done
Nux

@Nux คุณจำเป็นต้องใช้tput edมากกว่าtput el. ใช้ตัวอย่างของ @ อุ๋มed(บางทีเขาแก้ไขหลังจากที่คุณแสดงความคิดเห็น)
studgeek


13

วิธี \ 033 ไม่ได้ผลสำหรับฉัน วิธี \ r ใช้งานได้ แต่ไม่ได้ลบอะไรเลยเพียงแค่วางเคอร์เซอร์ไว้ที่จุดเริ่มต้นของบรรทัด ดังนั้นหากสตริงใหม่สั้นกว่าสตริงเก่าคุณจะเห็นข้อความที่เหลือท้ายบรรทัด ท้ายที่สุดแล้ว tput เป็นวิธีที่ดีที่สุด มีการใช้งานอื่น ๆ นอกเหนือจากสิ่งที่เคอร์เซอร์รวมทั้งติดตั้งไว้ล่วงหน้าใน Linux และ BSD distros จำนวนมากดังนั้นจึงควรมีให้สำหรับผู้ใช้ bash ส่วนใหญ่

#/bin/bash
tput sc # save cursor
printf "Something that I made up for this string"
sleep 1
tput rc;tput el # rc = restore cursor, el = erase to end of line
printf "Another message for testing"
sleep 1
tput rc;tput el
printf "Yet another one"
sleep 1
tput rc;tput el

นี่คือสคริปต์การนับถอยหลังเล็กน้อยที่จะเล่นด้วย:

#!/bin/bash
timeout () {
    tput sc
    time=$1; while [ $time -ge 0 ]; do
        tput rc; tput el
        printf "$2" $time
        ((time--))
        sleep 1
    done
    tput rc; tput ed;
}

timeout 10 "Self-destructing in %s"

นั่นเป็นการเคลียร์ทั้งบรรทัดจริงๆปัญหามันกระพริบตามากเกินไปสำหรับฉัน: '(
smarber

5

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

printf "\033[5A"

ซึ่งจะทำให้เคอร์เซอร์กระโดดขึ้น 5 บรรทัด จากนั้นคุณสามารถเขียนทับสิ่งที่คุณต้องการได้

หากไม่ได้ผลคุณสามารถลองprintf "\e[5A"หรือecho -e "\033[5A"ซึ่งควรมีผลเช่นเดียวกัน

โดยทั่วไปด้วยลำดับการหลีกเลี่ยงคุณสามารถควบคุมเกือบทุกอย่างในหน้าจอได้


สิ่งที่เทียบเท่าแบบพกพาคือtput cuu 5โดยที่ 5 คือจำนวนแถว ( cuuเพื่อเลื่อนขึ้นcudเพื่อเลื่อนลง)
Maëlan

4

ใช้อักขระส่งคืนแคร่:

echo -e "Foo\rBar" # Will print "Bar"

คำตอบนี้เป็นวิธีที่ง่ายที่สุด คุณยังสามารถบรรลุเอฟเฟกต์เดียวกันได้โดยใช้: printf "Foo \ rBar"
lee8oi

1

สามารถบรรลุได้โดยการวางแคร่กลับ \rจะประสบความสำเร็จโดยการวางกลับสายการบิน

ในโค้ดบรรทัดเดียวด้วย printf

for i in {10..1}; do printf "Counting down: $i\r" && sleep 1; done

หรือด้วย echo -ne

for i in {10..1}; do echo -ne "Counting down: $i\r" && sleep 1; done
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.