mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #890 from mdsn/follow-multiple
tail: Follow multiple files with the -f option
This commit is contained in:
commit
f82ba16dc5
5 changed files with 85 additions and 32 deletions
|
@ -147,22 +147,32 @@ pub fn uumain(args: Vec<String>) -> i32 {
|
||||||
unbounded_tail(buffer, &settings);
|
unbounded_tail(buffer, &settings);
|
||||||
} else {
|
} else {
|
||||||
let mut multiple = false;
|
let mut multiple = false;
|
||||||
let mut firstime = true;
|
let mut first_header = true;
|
||||||
|
let mut readers = Vec::new();
|
||||||
|
|
||||||
if files.len() > 1 {
|
if files.len() > 1 {
|
||||||
multiple = true;
|
multiple = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for file in &files {
|
for filename in &files {
|
||||||
if multiple {
|
if multiple {
|
||||||
if !firstime { println!(""); }
|
if !first_header { println!(""); }
|
||||||
println!("==> {} <==", file);
|
println!("==> {} <==", filename);
|
||||||
}
|
}
|
||||||
firstime = false;
|
first_header = false;
|
||||||
|
|
||||||
let path = Path::new(file);
|
let path = Path::new(filename);
|
||||||
let reader = File::open(&path).unwrap();
|
let file = File::open(&path).unwrap();
|
||||||
bounded_tail(reader, &settings);
|
bounded_tail(&file, &settings);
|
||||||
|
|
||||||
|
if settings.follow {
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
readers.push(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if settings.follow {
|
||||||
|
follow(readers, &files, &settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,16 +304,29 @@ fn obsolete(options: &[String]) -> (Vec<String>, Option<u64>) {
|
||||||
/// block read at a time.
|
/// block read at a time.
|
||||||
const BLOCK_SIZE: u64 = 1 << 16;
|
const BLOCK_SIZE: u64 = 1 << 16;
|
||||||
|
|
||||||
fn follow<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
|
fn follow<T: Read>(mut readers: Vec<BufReader<T>>, filenames: &Vec<String>, settings: &Settings) {
|
||||||
assert!(settings.follow);
|
assert!(settings.follow);
|
||||||
|
let mut last = readers.len() - 1;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
sleep(Duration::new(0, settings.sleep_msec*1000));
|
sleep(Duration::new(0, settings.sleep_msec*1000));
|
||||||
loop {
|
|
||||||
let mut datum = String::new();
|
for (i, reader) in readers.iter_mut().enumerate() {
|
||||||
match reader.read_line(&mut datum) {
|
// Print all new content since the last pass
|
||||||
Ok(0) => break,
|
loop {
|
||||||
Ok(_) => print!("{}", datum),
|
let mut datum = String::new();
|
||||||
Err(err) => panic!(err)
|
match reader.read_line(&mut datum) {
|
||||||
|
Ok(0) => break,
|
||||||
|
Ok(_) => {
|
||||||
|
if i != last {
|
||||||
|
println!("");
|
||||||
|
println!("==> {} <==", filenames[i]);
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
print!("{}", datum);
|
||||||
|
},
|
||||||
|
Err(err) => panic!(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,7 +335,7 @@ fn follow<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
|
||||||
/// Iterate over bytes in the file, in reverse, until `should_stop` returns
|
/// Iterate over bytes in the file, in reverse, until `should_stop` returns
|
||||||
/// true. The `file` is left seek'd to the position just after the byte that
|
/// true. The `file` is left seek'd to the position just after the byte that
|
||||||
/// `should_stop` returned true for.
|
/// `should_stop` returned true for.
|
||||||
fn backwards_thru_file<F>(file: &mut File, size: u64, buf: &mut Vec<u8>, delimiter: u8, should_stop: &mut F)
|
fn backwards_thru_file<F>(mut file: &File, size: u64, buf: &mut Vec<u8>, delimiter: u8, should_stop: &mut F)
|
||||||
where F: FnMut(u8) -> bool
|
where F: FnMut(u8) -> bool
|
||||||
{
|
{
|
||||||
assert!(buf.len() >= BLOCK_SIZE as usize);
|
assert!(buf.len() >= BLOCK_SIZE as usize);
|
||||||
|
@ -355,16 +378,8 @@ fn backwards_thru_file<F>(file: &mut File, size: u64, buf: &mut Vec<u8>, delimit
|
||||||
/// end of the file, and then read the file "backwards" in blocks of size
|
/// end of the file, and then read the file "backwards" in blocks of size
|
||||||
/// `BLOCK_SIZE` until we find the location of the first line/byte. This ends up
|
/// `BLOCK_SIZE` until we find the location of the first line/byte. This ends up
|
||||||
/// being a nice performance win for very large files.
|
/// being a nice performance win for very large files.
|
||||||
fn bounded_tail(mut file: File, settings: &Settings) {
|
fn bounded_tail(mut file: &File, settings: &Settings) {
|
||||||
let size = file.seek(SeekFrom::End(0)).unwrap();
|
let size = file.seek(SeekFrom::End(0)).unwrap();
|
||||||
if size == 0 {
|
|
||||||
if settings.follow {
|
|
||||||
let reader = BufReader::new(file);
|
|
||||||
follow(reader, settings);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf = vec![0; BLOCK_SIZE as usize];
|
let mut buf = vec![0; BLOCK_SIZE as usize];
|
||||||
|
|
||||||
// Find the position in the file to start printing from.
|
// Find the position in the file to start printing from.
|
||||||
|
@ -397,12 +412,6 @@ fn bounded_tail(mut file: File, settings: &Settings) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue following changes, if requested.
|
|
||||||
if settings.follow {
|
|
||||||
let reader = BufReader::new(file);
|
|
||||||
follow(reader, settings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unbounded_tail<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
|
fn unbounded_tail<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
|
||||||
|
@ -474,8 +483,10 @@ fn unbounded_tail<T: Read>(mut reader: BufReader<T>, settings: &Settings) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: make following stdin work with the new follow() signature
|
||||||
|
// maybe wrap stdin in a 1-element vec?
|
||||||
if settings.follow {
|
if settings.follow {
|
||||||
follow(reader, settings);
|
//follow(reader, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
tests/fixtures/tail/foobar2.txt
vendored
Normal file
2
tests/fixtures/tail/foobar2.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
un
|
||||||
|
deux
|
15
tests/fixtures/tail/foobar_follow_multiple.expected
vendored
Normal file
15
tests/fixtures/tail/foobar_follow_multiple.expected
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
==> foobar.txt <==
|
||||||
|
dos
|
||||||
|
tres
|
||||||
|
quattro
|
||||||
|
cinco
|
||||||
|
seis
|
||||||
|
siette
|
||||||
|
ocho
|
||||||
|
nueve
|
||||||
|
diez
|
||||||
|
once
|
||||||
|
|
||||||
|
==> foobar2.txt <==
|
||||||
|
un
|
||||||
|
deux
|
4
tests/fixtures/tail/foobar_follow_multiple_appended.expected
vendored
Normal file
4
tests/fixtures/tail/foobar_follow_multiple_appended.expected
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
==> foobar.txt <==
|
||||||
|
doce
|
||||||
|
trece
|
|
@ -8,6 +8,7 @@ use uu_tail::parse_size;
|
||||||
static UTIL_NAME: &'static str = "tail";
|
static UTIL_NAME: &'static str = "tail";
|
||||||
|
|
||||||
static FOOBAR_TXT: &'static str = "foobar.txt";
|
static FOOBAR_TXT: &'static str = "foobar.txt";
|
||||||
|
static FOOBAR_2_TXT: &'static str = "foobar2.txt";
|
||||||
static FOOBAR_WITH_NULL_TXT: &'static str = "foobar_with_null.txt";
|
static FOOBAR_WITH_NULL_TXT: &'static str = "foobar_with_null.txt";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -56,6 +57,26 @@ fn test_follow() {
|
||||||
child.kill().unwrap();
|
child.kill().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_follow_multiple() {
|
||||||
|
let (at, mut ucmd) = testing(UTIL_NAME);
|
||||||
|
let mut child = ucmd.arg("-f").arg(FOOBAR_TXT).arg(FOOBAR_2_TXT).run_no_wait();
|
||||||
|
|
||||||
|
let expected = at.read("foobar_follow_multiple.expected");
|
||||||
|
assert_eq!(read_size(&mut child, expected.len()), expected);
|
||||||
|
|
||||||
|
let first_append = "trois\n";
|
||||||
|
at.append(FOOBAR_2_TXT, first_append);
|
||||||
|
assert_eq!(read_size(&mut child, first_append.len()), first_append);
|
||||||
|
|
||||||
|
let second_append = "doce\ntrece\n";
|
||||||
|
let expected = at.read("foobar_follow_multiple_appended.expected");
|
||||||
|
at.append(FOOBAR_TXT, second_append);
|
||||||
|
assert_eq!(read_size(&mut child, expected.len()), expected);
|
||||||
|
|
||||||
|
child.kill().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_single_big_args() {
|
fn test_single_big_args() {
|
||||||
const FILE: &'static str = "single_big_args.txt";
|
const FILE: &'static str = "single_big_args.txt";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue