Buộc xả đầu ra vào một tệp trong khi tập lệnh bash vẫn đang chạy


89

Tôi có một tập lệnh nhỏ, được gọi hàng ngày bằng crontab bằng lệnh sau:

/homedir/MyScript &> some_log.log

Vấn đề với phương pháp này là some_log.log chỉ được tạo sau khi MyScript kết thúc. Tôi muốn chuyển đầu ra của chương trình vào tệp trong khi nó đang chạy để tôi có thể làm những việc như

tail -f some_log.log

và theo dõi tiến trình, v.v.


Chúng tôi sẽ cần phải có một -Hoặc mô tả nếu có thể số- những gì kịch bản nhỏ của bạn không chính xác ...
ChristopheD

7
Để giải nén các tập lệnh python, bạn có thể sử dụng "python -u". Để bỏ bộ đệm các tập lệnh perl, hãy xem câu trả lời của Greg Hewgill bên dưới. Và như vậy ...
Eloici

Nếu bạn có thể chỉnh sửa tập lệnh, bạn thường có thể xóa bộ đệm đầu ra một cách rõ ràng trong một tập lệnh, ví dụ như trong python với sys.stdout.flush().
drevicko

Câu trả lời:


28

bash chính nó sẽ không bao giờ thực sự ghi bất kỳ đầu ra nào vào tệp nhật ký của bạn. Thay vào đó, các lệnh mà nó gọi ra như một phần của tập lệnh sẽ viết đầu ra riêng lẻ và tuôn ra bất cứ khi nào họ cảm thấy thích. Vì vậy, câu hỏi của bạn thực sự là làm thế nào để buộc các lệnh trong bash script tuôn ra, và điều đó phụ thuộc vào chúng là gì.


23
Tôi thực sự không hiểu câu trả lời này.
Alfonso Santiago

2
Để biết rõ hơn tại sao đầu ra tiêu chuẩn lại hoạt động như vậy, hãy xem stackoverflow.com/a/13933741/282728 . Một phiên bản ngắn — theo mặc định, nếu được chuyển hướng đến một tệp, stdout được lưu vào bộ đệm hoàn toàn; nó được ghi vào một tệp chỉ sau một lần xả. Stderr thì không — nó được viết sau mỗi '\ n'. Một giải pháp là sử dụng lệnh 'script' do user3258569 đề xuất bên dưới, để stdout được tuôn ra sau mỗi lần kết thúc dòng.
Alex

Nói rõ ràng, và mười năm sau, nhưng đây là một nhận xét, không phải là một câu trả lời và nó không nên là câu trả lời được chấp nhận.
RealHandy

84

Tôi đã tìm thấy một giải pháp cho điều này ở đây . Sử dụng ví dụ của OP về cơ bản bạn chạy

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

và sau đó bộ đệm được tuôn ra sau mỗi dòng đầu ra. Tôi thường kết hợp điều này với nohupđể chạy các công việc dài trên một máy tính từ xa.

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

Bằng cách này, quy trình của bạn không bị hủy khi bạn đăng xuất.


1
Bạn có thể thêm một liên kết đến một số tài liệu cho stdbuf? Dựa trên nhận xét này, có vẻ như nó không có sẵn trên một số bản phân phối. Bạn có thể làm rõ?
Vụ kiện của Fund Monica vào

1
stdbuf -o điều chỉnh bộ đệm stdout. Các tùy chọn khác là -i và -e cho stdin và stderr. L đặt bộ đệm dòng. Người ta cũng có thể chỉ định kích thước bộ đệm hoặc 0 để không có bộ đệm.
Seppo Enarvi

5
liên kết đó không còn nữa.
Fzs2

2
@NicHartley: stdbuflà một phần của coreutils GNU, tài liệu có thể được tìm thấy tại gnu.org
Thor

Trong trường hợp nó giúp ích cho bất kỳ ai, hãy sử dụng export -f my_function và sau đó stdbuf -oL bash -c "my_function -args"nếu bạn cần chạy một hàm thay vì một tập lệnh
Anonymous

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

Khóa là -f. Trích dẫn từ man script:

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

Chạy trong nền:

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

Chà! Một giải pháp hiệu quả busybox! (vỏ của tôi bị đóng băng sau đó, nhưng bất cứ điều gì)
Victor Sergienko

9

Bạn có thể sử dụng teeđể ghi vào tệp mà không cần phải xả.

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

2
Điều này vẫn đệm đầu ra, ít nhất là trong môi trường Ubuntu 18.04 của tôi. Nội dung cuối cùng cũng được ghi vào tệp theo cách nào đó, nhưng tôi nghĩ OP đang yêu cầu một phương pháp mà họ có thể theo dõi tiến trình chính xác hơn trước khi tệp được ghi xong và phương pháp này không cho phép điều đó hơn là chuyển hướng đầu ra. làm.
mltsy

3

Đây không phải là một chức năng của bash, vì tất cả những gì shell làm là mở tệp được đề cập và sau đó chuyển bộ mô tả tệp làm đầu ra tiêu chuẩn của tập lệnh. Những gì bạn cần làm là đảm bảo đầu ra được xóa khỏi tập lệnh của bạn thường xuyên hơn hiện tại.

Trong Perl chẳng hạn, điều này có thể được thực hiện bằng cách thiết lập:

$| = 1;

Xem perlvar để biết thêm thông tin về điều này.


2

Việc đệm đầu ra phụ thuộc vào cách chương trình của bạn /homedir/MyScriptđược triển khai. Nếu bạn thấy rằng đầu ra đang bị lưu vào bộ đệm, bạn phải buộc nó trong quá trình triển khai của mình. Ví dụ: sử dụng sys.stdout.flush () nếu là chương trình python hoặc sử dụng fflush (stdout) nếu là chương trình C.


2

Điều này sẽ giúp đỡ?

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

Điều này sẽ ngay lập tức hiển thị các mục nhập duy nhất từ ​​access.log bằng tiện ích stdbuf .


Rắc rối duy nhất là stdbuf dường như là một tiện ích cũ không có sẵn trên các bản phân phối mới.
Ondra Žižka

..nor trong hộp thư bận của tôi :(
Campa

Trên thực tế, tôi có stdbufsẵn trong Ubuntu vào lúc này, không chắc tôi đã lấy nó ở đâu.
Ondra Žižka

Tôi có stdbuf trong Centos 7.5
Tối đa

1
Trong Ubuntu 18.04, stdbuflà một phần của coreutilus(tìm thấy với apt-file search /usr/bin/stdbuf).
Rmano

1

Làm thế nào chỉ được phát hiện ở đây vấn đề là bạn phải đợi rằng các chương trình mà bạn chạy từ tập lệnh của bạn hoàn thành công việc của chúng.
Nếu trong tập lệnh của bạn, bạn chạy chương trình ở chế độ nền, bạn có thể thử thêm điều gì đó.

Nói chung, một cuộc gọi tới synctrước khi bạn thoát cho phép xóa bộ đệm hệ thống tệp và có thể giúp ích một chút.

Nếu trong tập lệnh, bạn khởi động một số chương trình ở chế độ nền ( &), bạn có thể đợi chúng kết thúc trước khi thoát khỏi tập lệnh. Để có ý tưởng về cách nó có thể hoạt động, bạn có thể xem bên dưới

#!/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
# ...

waithoạt động với các công việc cũng như với các PIDcon số, một giải pháp lười biếng nên được đặt ở cuối tập lệnh

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

Khó khăn hơn là tình huống nếu bạn chạy một thứ gì đó chạy một thứ khác trong nền vì bạn phải tìm kiếm và chờ đợi (nếu đúng như vậy) khi kết thúc tất cả quy trình con : ví dụ nếu bạn chạy một daemon thì có lẽ không phải vậy. để đợi nó kết thúc :-).

Ghi chú:

  • wait $ {!} có nghĩa là "đợi cho đến khi quá trình nền cuối cùng hoàn tất", đây $!là PID của quá trình nền cuối cùng. Vì vậy, đặt wait ${!}ngay sau program_2 &tương đương với việc thực thi trực tiếp program_2mà không cần gửi nó trong nền với&

  • Từ sự giúp đỡ của wait:

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

1

Cảm ơn @user3258569, kịch bản có lẽ là thứ duy nhất hoạt động busybox!

Tuy nhiên, chiếc vỏ đã đóng băng đối với tôi sau khi nó. Đang tìm kiếm nguyên nhân, tôi thấy các cảnh báo lớn màu đỏ này "không sử dụng trong trình bao không tương tác" trong trang hướng dẫn tập lệnh :

scriptđược thiết kế chủ yếu cho các phiên đầu cuối tương tác. Khi stdin không phải là một thiết bị đầu cuối (ví dụ echo foo | script:), thì phiên có thể bị treo, vì trình bao tương tác trong phiên tập lệnh bỏ sót EOFscriptkhông có manh mối khi nào đóng phiên. Xem phần GHI CHÚ để biết thêm thông tin.

Thật. script -c "make_hay" -f /dev/null | grep "needle"đã đóng băng vỏ cho tôi.

Ngược lại với cảnh báo, tôi nghĩ rằng echo "make_hay" | scriptSẼ vượt qua EOF, vì vậy tôi đã thử

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

va no đa hoạt động!

Lưu ý các cảnh báo trong trang người đàn ông. Điều này có thể không hiệu quả với bạn.


0

thay thế cho stdbuf là awk '{print} END {fflush()}' tôi ước có một nội trang bash để làm điều này. Thông thường thì không cần thiết, nhưng với các phiên bản cũ hơn, có thể có lỗi đồng bộ hóa bash trên bộ mô tả tệp.


-2

Tôi không biết nếu nó sẽ hoạt động, nhưng những gì về việc gọi điện sync?


1
synclà hoạt động của hệ thống tệp ở mức thấp và không liên quan đến đầu ra được đệm ở mức ứng dụng.
Greg Hewgill

2
syncghi bất kỳ bộ đệm hệ thống tệp bẩn nào vào bộ nhớ vật lý, nếu cần. Đây là nội bộ của hệ điều hành; các ứng dụng chạy trên hệ điều hành luôn nhìn thấy một cái nhìn nhất quán về hệ thống tệp cho dù các khối đĩa đã được ghi vào bộ nhớ vật lý hay chưa. Đối với câu hỏi ban đầu, ứng dụng (script) có thể đang đệm đầu ra trong bộ đệm bên trong ứng dụng và hệ điều hành thậm chí sẽ không biết (chưa) rằng đầu ra thực sự được ghi vào stdout. Vì vậy, hoạt động kiểu "đồng bộ hóa" giả định sẽ không thể "truy cập" vào tập lệnh và kéo dữ liệu ra ngoài.
Greg Hewgill

-2

Tôi gặp sự cố này với quy trình nền trong Mac OS X bằng cách sử dụng StartupItems. Đây là cách tôi giải quyết nó:

Nếu tôi thực hiện, sudo ps auxtôi có thể thấy nó mytoolđược khởi chạy.

Tôi thấy rằng (do bộ đệm) khi Mac OS X tắt mytoolkhông bao giờ chuyển đầu ra sang sedlệnh. Tuy nhiên, nếu tôi thực hiện sudo killall mytool, sau đó mytoolchuyển đầu ra cho sedlệnh. Do đó, tôi đã thêm một stoptrường hợp StartupItemsđược thực thi khi Mac OS X tắt:

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

Đây thực sự không phải là một câu trả lời hay cho Freeman vì nó rất cụ thể đối với môi trường của bạn. OP muốn giám sát sản lượng không giết nó.
Grey

-3

dù muốn hay không thì đây là cách hoạt động của chuyển hướng.

Trong trường hợp của bạn, đầu ra (có nghĩa là tập lệnh của bạn đã hoàn thành) của tập lệnh của bạn được chuyển hướng đến tệp đó.

Những gì bạn muốn làm là thêm những chuyển hướng đó trong tập lệnh của bạn.

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.