如何从文件中读取行并等待添加新行,就像 tail -f 那样

Avn*_*arr 4 rust

我正在从这样的文件中读取行:

let file = File::open("/tmp/log.txt").unwrap();
let reader = BufReader::new(&file);

for line in reader.lines() {
 // process lines
}
Run Code Online (Sandbox Code Playgroud)

从另一个进程我附加到文件

echo "hello" >> /tmp/log.txt
echo "world" >> /tmp/log.txt
...
Run Code Online (Sandbox Code Playgroud)

一段时间后,可以将更多消息附加到日志中。

目前我遇到一个问题,一旦读取了所有当前行,循环就会退出。我希望它等待(阻塞),直到更多行被推送到日志中。

我注意到,如果我 open stdin()for循环将阻塞并等待标准输入输入更多行。

有没有办法实现相同的行为?

Val*_*tin 8

您可以使用notifycrate来监视文件更改。然后,如果您跟踪文件的大小,则可以在每次更改后使用该特征Seekseek(SeekFrom::Start(last_file_end))并且仅读取新内容。

要重新创建tail -f /tmp/log.txt,您可以执行以下操作:

use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
use std::fs::{self, File};
use std::io::{Read, Seek, SeekFrom};
use std::sync::mpsc;
use std::time::Duration;

fn main() {
    let path = std::env::args().nth(1).unwrap();

    let (tx, rx) = mpsc::channel();
    let mut watcher = watcher(tx, Duration::from_millis(100)).unwrap();
    watcher.watch(&path, RecursiveMode::NonRecursive).unwrap();

    let mut contents = fs::read_to_string(&path).unwrap();
    let mut pos = contents.len() as u64;

    print!("{}", contents);

    loop {
        match rx.recv() {
            Ok(DebouncedEvent::Write(_)) => {
                let mut f = File::open(&path).unwrap();
                f.seek(SeekFrom::Start(pos)).unwrap();

                pos = f.metadata().unwrap().len();

                contents.clear();
                f.read_to_string(&mut contents).unwrap();

                print!("{}", contents);
            }
            Ok(_) => {}
            Err(err) => {
                eprintln!("Error: {:?}", err);
                std::process::exit(1);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,通过 do 运行它cargo run -- /tmp/log.txt会复制 的行为tail -f /tmp/log.txt,其中它打印文件的内容,并随后在附加时输出其他内容。

请注意,它不处理截断或删除。在这种情况下,请检查DebouncedEvent枚举变体,并根据需要进行修改。此外,unwraps 可能应该处理得更好一些。


当然,lines()如果您愿意,您仍然可以使用:

let mut f = File::open(&path).unwrap();
f.seek(SeekFrom::Start(pos)).unwrap();

pos = f.metadata().unwrap().len();

let reader = BufReader::new(f);
for line in reader.lines() {
    println!("> {:?}", line);
}
Run Code Online (Sandbox Code Playgroud)