0

I have a directory full of log files and I would like a bash oneline command to follow the latest log file that matches a pattern (e.g. "logfile*"). Basically I have a line that should work, just it does not...

tail -f $(ls -1rt logfile* | tail -n 1)

When I test only part of the command like so

ls -1rt logfile* | tail -n 1
logfile_20210111_105242.log

It will give me the latest log filename that I want to follow:

but adding the tail -f I get following response:

tail -f $(ls -1rt logfile* | tail -n 1)
tail: cannot open ''$'\033''[0m'$'\033''[00mlogfile_20210111_105242.log'$'\033''[0m' for reading: No such file or directory

Any ideas on how to get this to work?

EDIT: Also, any idea how best to use this in an alias with parameters? most reasearch indicates a function in .bashrc has to be used, but I also found someone saying it should be possible by using !:1 as variables.

EDIT2: Solution

Step1: use the solution from comments below

tail -f $(find . -maxdepth 1 -name 'logfile*' -printf '%Ts/%f\n' | sort -n | tail -1 | cut -d/ -f2)

Step2: put it in .alias and make it work with a paramter for a filepattern

alias tailf="tail -f $(find . -maxdepth 1 -name '\!:1' -printf '%Ts/%f\n' | sort -n | tail -1 | cut -d/ -f2)"

EDIT3: While the alias seems to work, unfortunately it will tail all files matching the pattern passed as parameter. When I hardcode the pattern in the alias definition it works perfectly just tailing the latest file matching the pattern.

Thanks

Phil
  • 497
  • 1
  • 6
  • 16
  • 5
    You've got color codes in your filename. Let me guess, you have `ls` aliased to set `--color=always`? Turn that off; the `--color=auto` default is there for good reasons. – Charles Duffy Jan 13 '21 at 16:01
  • 2
    That said, it's deeply inappropriate to use `ls` for this purpose at all; see [Why you should never parse `ls`](https://mywiki.wooledge.org/ParsingLs). – Charles Duffy Jan 13 '21 at 16:02
  • 2
    ...[BashFAQ #3](https://mywiki.wooledge.org/BashFAQ/003) describes how to _safely_ find the newest file in a directory, in a way that (unlike `ls`) will work for all possible filenames. "All possible filenames" is a wide scope -- filenames can contain spaces, newlines, quotes, unicode characters; it's unwise to write code that makes assumptions. – Charles Duffy Jan 13 '21 at 16:03
  • Indeed there is color coding, but it's a company server so this change might not be welcomed. Thanks for the the links, very good general info! – Phil Jan 14 '21 at 11:42

1 Answers1

2
tail -f "$(find . -maxdepth 1 -name "logfile*" -printf "%Ts/%f\n" | sort -n | tail -1 | cut -d/ -f2)"

Tail the result of the find command. Search for files prefixed with logfile in the current directory and print the epoch time of creation as well as the file path and name, separated by a forward slash Pipe this through to sort and then print the latest entry with tail -1 before stripping out to to leave only the file path with cut.

Raman Sailopal
  • 12,320
  • 2
  • 11
  • 18
  • What if my filename contains a comma, though? ;) – Benjamin W. Jan 13 '21 at 16:14
  • Actually, in this case, the path is not required as we are already in the directory and so I have amended with "/" as the separator – Raman Sailopal Jan 13 '21 at 16:15
  • 2
    Why not write the answer to use NULs, which _can't possibly_ exist in filenames, instead of newlines (which can)? You're already assuming GNU `find`; as long as you also assume GNU `sort` and `tail`, support for NUL delimiters is built-in. – Charles Duffy Jan 13 '21 at 16:17
  • Thanks, that worked great. I even found how to make this work as alias with parameters: `alias tailf="tail -f $(find . -maxdepth 1 -name '\!:1' -printf '%Ts/%f\n' | sort -n | tail -1 | cut -d/ -f2)"` so I can just call it like `tailf logfile_202001*`for instance. – Phil Jan 14 '21 at 12:33
  • I'm happy to help. If you would kindly accept the solution as an answer, it would be greatly appreciated. – Raman Sailopal Jan 14 '21 at 12:34
  • Will do that... just found out though, that there seems to be an issue with the parameter/variable: While the alias seems to work, unfortunately it will tail all files matching the pattern passed as parameter. When I hardcode the pattern in the alias definition it works perfectly just tailing the latest file matching the pattern. – Phil Jan 14 '21 at 14:13
  • @Phil, in general, if you want to take parameters, you should be using a function instead of an alias. Aliases can't really take parameters at all -- they're just prefix substitution, whereas a function can be written to handle parameters however you want it to. – Charles Duffy Jan 14 '21 at 23:22
  • @Phil, ...so, for example, you could start your function with `local -a findArgs=( ); (( $# )) || set -- 'logfile*'; for arg do findArgs+=( -name "$arg" -o ); done`, and then use `find . -maxdepth 1 '(' "${findArgs[@]}" -false ')'` to substitute each argument into the `find` command. – Charles Duffy Jan 14 '21 at 23:26
  • @Phil, ...anyhow, if you need more guidance than that, I'd suggest asking a new question, since this one has already been adequately answered. – Charles Duffy Jan 14 '21 at 23:26
  • @CharlesDuffy Thanks, I asked the remaining question here: [link](https://stackoverflow.com/questions/65825241/alias-with-parameter-behaves-differently-than-its-underlying-command) – Phil Jan 21 '21 at 10:16