บังคับให้ล้างเอาต์พุตไปยังไฟล์ในขณะที่สคริปต์ bash ยังทำงานอยู่


90

ฉันมีสคริปต์เล็ก ๆ ซึ่งเรียกว่าทุกวันโดย crontab โดยใช้คำสั่งต่อไปนี้:

/homedir/MyScript &> some_log.log

ปัญหาของวิธีนี้คือ some_log.log ถูกสร้างขึ้นหลังจาก MyScript เสร็จสิ้นเท่านั้น ฉันต้องการล้างผลลัพธ์ของโปรแกรมลงในไฟล์ในขณะที่กำลังทำงานอยู่เพื่อที่ฉันจะได้ทำสิ่งต่างๆเช่น

tail -f some_log.log

และติดตามความคืบหน้า ฯลฯ


เราจะต้องมีคำอธิบายหรือโค้ดที่เป็นไปได้ - ว่าสคริปต์ขนาดเล็กของคุณทำอะไรได้บ้าง ...
ChristopheD

7
ในการเลิกบัฟเฟอร์สคริปต์ python คุณสามารถใช้ "python -u" หากต้องการยกเลิกการบัฟเฟอร์สคริปต์ perl โปรดดูคำตอบของ Greg Hewgill ด้านล่าง และอื่น ๆ ...
Eloici

หากคุณสามารถแก้ไขสคริปต์ได้โดยปกติแล้วคุณสามารถล้างบัฟเฟอร์เอาต์พุตได้อย่างชัดเจนภายในสคริปต์ตัวอย่างเช่นใน python ด้วยsys.stdout.flush().
drevicko

คำตอบ:


28

bash จะไม่เขียนผลลัพธ์ใด ๆ ลงในไฟล์บันทึกของคุณ แต่คำสั่งที่เรียกใช้เป็นส่วนหนึ่งของสคริปต์จะเขียนเอาต์พุตทีละรายการและล้างเมื่อใดก็ตามที่รู้สึกต้องการ ดังนั้นคำถามของคุณคือการบังคับให้คำสั่งภายใน bash script ล้างอย่างไรและขึ้นอยู่กับสิ่งที่พวกเขาเป็น


25
ฉันไม่เข้าใจคำตอบนี้จริงๆ
Alfonso Santiago

3
สำหรับความคิดที่ดีว่าทำไมพฤติกรรมออกมาตรฐานเช่นนี้, ตรวจสอบstackoverflow.com/a/13933741/282728 เวอร์ชันสั้น - โดยค่าเริ่มต้นหากเปลี่ยนเส้นทางไปยังไฟล์ stdout จะถูกบัฟเฟอร์ทั้งหมด มันถูกเขียนลงในไฟล์หลังจากที่ฟลัชเท่านั้น Stderr ไม่ได้เขียนไว้หลังทุก '\ n' วิธีแก้ไขอย่างหนึ่งคือการใช้คำสั่ง 'script' ที่แนะนำโดย user3258569 ด้านล่างเพื่อให้ stdout ล้างหลังจากทุกบรรทัด
Alex

2
ระบุสิ่งที่ชัดเจนและสิบปีต่อมา แต่นี่เป็นความคิดเห็นไม่ใช่คำตอบและไม่ควรเป็นคำตอบที่ยอมรับได้
RealHandy

87

ผมพบว่าวิธีการแก้ปัญหานี้ที่นี่ ใช้ตัวอย่างของ OP ที่คุณเรียกใช้โดยทั่วไป

stdbuf -oL /homedir/MyScript &> some_log.log

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

stdbuf -oL nohup /homedir/MyScript &> some_log.log

วิธีนี้กระบวนการของคุณจะไม่ถูกยกเลิกเมื่อคุณออกจากระบบ


1
คุณช่วยเพิ่มลิงค์ไปยังเอกสารประกอบสำหรับstdbuf? จากความคิดเห็นนี้ดูเหมือนว่าจะไม่มีให้บริการในโรงกลั่นบางแห่ง คุณช่วยชี้แจงได้ไหม
คดีของ Fund Monica

1
stdbuf -o ปรับ stdout buffering ตัวเลือกอื่น ๆ คือ -i และ -e สำหรับ stdin และ stderr L ตั้งค่าการบัฟเฟอร์บรรทัด นอกจากนี้ยังสามารถระบุขนาดบัฟเฟอร์หรือ 0 หากไม่มีการบัฟเฟอร์
Seppo Enarvi

5
ลิงก์นั้นไม่สามารถใช้ได้อีกต่อไป
john-jones

2
@NicHartley: stdbufเป็นส่วนหนึ่งของ GNU coreutils เอกสารสามารถพบได้ที่ gnu.org
ธ .

ในกรณีที่ช่วยใครได้ให้ใช้ export -f my_functionและstdbuf -oL bash -c "my_function -args"ถ้าคุณต้องการเรียกใช้ฟังก์ชันแทนสคริปต์
ไม่ระบุชื่อ

29
script -c <PROGRAM> -f OUTPUT.txt

คีย์คือ -f. อ้างจากสคริปต์คน:

-f, --flush
     Flush output after each write.  This is nice for telecooperation: one person
     does 'mkfifo foo; script -f foo', and another can supervise real-time what is
     being done using 'cat foo'.

ทำงานในพื้นหลัง:

nohup script -c <PROGRAM> -f OUTPUT.txt

ว้าว! ทางออกที่ใช้ได้ผลbusybox! (เปลือกของฉันค้างหลังจากนั้น แต่อะไรก็ตาม)
Victor Sergienko

9

คุณสามารถใช้teeเขียนลงไฟล์ได้โดยไม่ต้องใช้ฟลัชชิ่ง

/homedir/MyScript 2>&1 | tee some_log.log > /dev/null

2
สิ่งนี้ยังคงบัฟเฟอร์เอาต์พุตอย่างน้อยในสภาพแวดล้อม Ubuntu 18.04 ของฉัน ในที่สุดเนื้อหาจะถูกเขียนไปยังไฟล์ไม่ทางใดก็ทางหนึ่ง แต่ฉันคิดว่า OP กำลังขอวิธีที่พวกเขาสามารถตรวจสอบความคืบหน้าได้อย่างแม่นยำมากขึ้นก่อนที่ไฟล์จะเขียนเสร็จและวิธีนี้ไม่อนุญาตให้มีการเปลี่ยนทิศทางเอาต์พุตมากกว่า ทำ.
mltsy

3

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

ตัวอย่างเช่นใน Perl สิ่งนี้สามารถทำได้โดยการตั้งค่า:

$| = 1;

ดูperlvarสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้


2

การบัฟเฟอร์เอาต์พุตขึ้นอยู่กับวิธี/homedir/MyScriptการใช้งานโปรแกรมของคุณ หากคุณพบว่าเอาต์พุตได้รับการบัฟเฟอร์คุณต้องบังคับใช้ในการนำไปใช้งาน ตัวอย่างเช่นใช้ sys.stdout.flush () ถ้าเป็นโปรแกรม python หรือใช้ fflush (stdout) ถ้าเป็นโปรแกรม C


2

สิ่งนี้จะช่วยได้ไหม

tail -f access.log | stdbuf -oL cut -d ' ' -f1 | uniq 

นี้ทันทีจะแสดงรายการที่ไม่ซ้ำกันจาก access.log ใช้ยูทิลิตี้ stdbuf


ปัญหาเดียวคือ stdbuf น่าจะเป็นยูทิลิตี้เก่าบางตัวที่ไม่มีใน distros ใหม่
Ondra Žižka

.. หรือใน busybox ของฉัน :(
Campa

อันที่จริงฉันมีstdbufใน Ubuntu ในขณะนี้ไม่แน่ใจว่าได้รับจากที่ไหน
Ondra Žižka

ฉันมี stdbuf ใน Centos 7.5
สูงสุด

1
ใน Ubuntu 18.04 stdbufเป็นส่วนหนึ่งของcoreutilus(found with apt-file search /usr/bin/stdbuf)
Rmano

1

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

โดยทั่วไปการโทรไปยังsyncก่อนที่คุณจะออกจะอนุญาตให้ล้างบัฟเฟอร์ระบบไฟล์และสามารถช่วยได้เล็กน้อย

หากในสคริปต์คุณเริ่มบางโปรแกรมในพื้นหลัง ( &) คุณสามารถรอให้โปรแกรมเสร็จสิ้นก่อนที่คุณจะออกจากสคริปต์ หากต้องการทราบวิธีการทำงานคุณสามารถดูด้านล่าง

#!/bin/bash
#... some stuffs ...
program_1 &          # here you start a program 1 in background
PID_PROGRAM_1=${!}   # here you remember its PID
#... some other stuffs ... 
program_2 &          # here you start a program 2 in background
wait ${!}            # You wait it finish not really useful here
#... some other stuffs ... 
daemon_1 &           # We will not wait it will finish
program_3 &          # here you start a program 1 in background
PID_PROGRAM_3=${!}   # here you remember its PID
#... last other stuffs ... 
sync
wait $PID_PROGRAM_1
wait $PID_PROGRAM_3  # program 2 is just ended
# ...

เนื่องจากใช้waitงานได้กับงานและPIDตัวเลขการแก้ปัญหาที่ขี้เกียจควรวางไว้ที่ส่วนท้ายของสคริปต์

for job in `jobs -p`
do
   wait $job 
done

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

บันทึก:

  • รอ $ {!} หมายถึง "รอจนกว่ากระบวนการพื้นหลังสุดท้ายจะเสร็จสิ้น" โดยที่$!PID ของกระบวนการพื้นหลังสุดท้ายอยู่ที่ไหน ดังนั้นการใส่wait ${!}เพียงหลังprogram_2 &จะเทียบเท่ากับการดำเนินการโดยตรงprogram_2โดยไม่ต้องส่งในพื้นหลังด้วย&

  • จากความช่วยเหลือของwait:

    Syntax    
        wait [n ...]
    Key  
        n A process ID or a job specification
    

1

ขอบคุณ@user3258569สคริปต์อาจเป็นสิ่งเดียวที่ใช้ได้busybox !

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

scriptได้รับการออกแบบมาสำหรับเซสชันเทอร์มินัลแบบโต้ตอบเป็นหลัก เมื่อ stdin ไม่ใช่เทอร์มินัล (ตัวอย่างเช่นecho foo | script:) เซสชันสามารถแฮงค์ได้เนื่องจากเชลล์แบบโต้ตอบภายในเซสชันสคริปต์ขาดEOFและscriptไม่มีเงื่อนงำว่าจะปิดเซสชันเมื่อใด ดูหมายเหตุสำหรับข้อมูลเพิ่มเติม

จริง. script -c "make_hay" -f /dev/null | grep "needle"กำลังแช่แข็งเปลือกให้ฉัน

นับเป็นการเตือนฉันคิดว่าecho "make_hay" | scriptจะผ่าน EOF ดังนั้นฉันจึงลอง

echo "make_hay; exit" | script -f /dev/null | grep 'needle'

และมันได้ผล!

สังเกตคำเตือนใน man page วิธีนี้อาจไม่ได้ผลสำหรับคุณ


0

ทางเลือกอื่นสำหรับ stdbuf คือawk '{print} END {fflush()}' ฉันหวังว่าจะมี bash builtin เพื่อทำสิ่งนี้ โดยปกติไม่จำเป็น แต่สำหรับเวอร์ชันเก่าอาจมีข้อบกพร่องในการซิงโครไนซ์ bash ในตัวอธิบายไฟล์


-2

ฉันไม่รู้ว่ามันจะใช้ได้syncหรือเปล่าแต่โทรมาล่ะ?


1
syncเป็นการดำเนินการระบบไฟล์ระดับต่ำและไม่เกี่ยวข้องกับเอาต์พุตบัฟเฟอร์ที่ระดับแอ็พพลิเคชัน
Greg Hewgill

2
syncเขียนบัฟเฟอร์ระบบไฟล์สกปรกไปยังหน่วยเก็บข้อมูลทางกายภาพหากจำเป็น สิ่งนี้อยู่ภายในระบบปฏิบัติการ แอปพลิเคชันที่ทำงานบน OS จะเห็นมุมมองที่สอดคล้องกันของระบบไฟล์เสมอว่าบล็อกดิสก์ถูกเขียนไปยังหน่วยเก็บข้อมูลจริง สำหรับคำถามเดิมแอปพลิเคชัน (สคริปต์) อาจจะบัฟเฟอร์เอาต์พุตในบัฟเฟอร์ภายในของแอปพลิเคชันและระบบปฏิบัติการจะไม่รู้ด้วยซ้ำ (ยัง) ว่าเอาต์พุตถูกกำหนดให้เขียนไปยัง stdout ดังนั้นการดำเนินการประเภท "sync" แบบสมมุติจะไม่สามารถ "เข้าถึง" สคริปต์และดึงข้อมูลออกมาได้
Greg Hewgill

-2

ฉันมีปัญหากับกระบวนการพื้นหลังใน Mac OS X โดยใช้ไฟล์StartupItems. นี่คือวิธีแก้ปัญหา:

ถ้าฉันทำให้sudo ps auxฉันเห็นว่าmytoolเปิดตัว

ฉันพบว่า (เนื่องจากการบัฟเฟอร์) เมื่อ Mac OS X ปิดตัวลงจะmytoolไม่ถ่ายโอนผลลัพธ์ไปยังsedคำสั่ง อย่างไรก็ตามหากฉันดำเนินการsudo killall mytoolให้mytoolโอนเอาต์พุตไปยังsedคำสั่ง ดังนั้นฉันจึงเพิ่มstopกรณีStartupItemsที่จะดำเนินการเมื่อ MacOS X ปิดตัวลง:

start)
    if [ -x /sw/sbin/mytool ]; then
      # run the daemon
      ConsoleMessage "Starting mytool"
      (mytool | sed .... >> myfile.txt) & 
    fi
    ;;
stop)
    ConsoleMessage "Killing mytool"
    killall mytool
    ;;

นี่ไม่ใช่คำตอบที่ดีฟรีแมนเนื่องจากเป็นคำตอบที่เฉพาะเจาะจงกับสภาพแวดล้อมของคุณ OP ต้องการตรวจสอบเอาท์พุทไม่ฆ่ามัน
สีเทา

-3

ชอบหรือไม่นี่คือวิธีการทำงานของการเปลี่ยนเส้นทาง

ในกรณีของคุณผลลัพธ์ (หมายถึงสคริปต์ของคุณเสร็จสิ้นแล้ว) ของสคริปต์ของคุณถูกเปลี่ยนเส้นทางไปยังไฟล์นั้น

สิ่งที่คุณต้องการทำคือเพิ่มการเปลี่ยนเส้นทางเหล่านั้นในสคริปต์ของคุณ

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