1

Would there be a way to use the output of ls -ltr with xargs?

Assume that the result of ls is one file as below.

ls -ltr | tail -1 | xargs -I{} open {}

For comparison, I use the following command to use find to move files:

find ~/Downloads/ -iname '*hello*' -print0 | xargs -0 -I{} mv {} ./

Can we somehow use NUL character?


[Solved]
As mentioned in the @illkachu's comments, the following works but is not suggested.

somecmd "$(ls -tr | tail -1)"

What Stéphane suggests seem be alternatives.

  • 3
    Using the output of ls is number 1 in BashPitfalls. If you can tell what you want to do, we might be able to point to a better solution – ilkkachu Nov 29 '17 at 22:19
  • Maybe this Q if you want to process one or more of the newest files? remove oldest files. At least take a peek at the zsh solutions. – ilkkachu Nov 29 '17 at 22:28
  • It is generally a really bad idea to parse the output of ls. You should probably look into either using find or simple shell globbing to get your list of files to process. Extensive further reading on the subject can be found here. – DopeGhoti Nov 29 '17 at 22:32
  • 1
    @villybyun, for one file: somecmd "$(ls -tr | tail -1)" -- That will burn in a number of edge cases, most of which involve dashes, control characters or newlines in file names. I already feel like I've done something horrible, just writing that. – ilkkachu Nov 29 '17 at 22:38
1

Generally, you wouldn't as the output of ls is not post-processable reliably.

For instance:

open -- "$(ls -t | head -n 1)"

doesn't work if the name of newest file contains newline characters.

Ideally, you'd want:

ls -zt | head -zn1 | xargs -r0 open --

But unfortunately, I don't know of any ls implementation that supports a -z/-0 option, and the maintainers of GNU ls at least declined several times to add it.

Now, there are other ways with some implementations.

With the GNU and ast-open implementations of ls, in the C locale, the output with the --quoting-style=shell-always option is post-processable by zsh, ksh93 or bash:

export LC_ALL=C
eval "files=($(ls --quoting-style=shell-always -t))"

open -- "${files[0]}" # $files[1] with zsh

Though in zsh, you'd just do

open ./*(om[1])

or:

open ./Ctrl+Xm

as zsh has built-in support to sort files by modification time (and many other criteria that go far beyond ls capabilities).

FreeBSD ls has a --libxo option to generate json or xml output, so you could do:

ls --libxo=json -t | perl -MJSON::PP -0777 -l0 -ne '
  print decode_json($_)
    ->{"file-information"}
    ->{directory}->[0]
    ->{entry}->[0]
    ->{name}' | xargs -r0 open --

But then it would be simpler to do the whole thing in perl.

  • I don't see the issue with using tr to replace tabs for nulls ls -A | tr '\n' '\0' | xargs -L 1 -0 echo – Ray Foss Jun 12 at 15:03
  • @RayFoss, you're missing the point. That still doesn't work for file names that contain newline characters. – Stéphane Chazelas Jun 12 at 16:25
1

As you already have been told: In general this is not the approach you want to chose. However, as a start:

ls -ltr | awk '/^-/ { print $9; }' | xargs

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.