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.

villybyun
  • 13
  • 5
  • 3
    Using the output of `ls` is [number 1 in BashPitfalls](http://mywiki.wooledge.org/BashPitfalls#for_i_in_.24.28ls_.2A.mp3.29). 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
  • Also: [BashFAQ 099: How can I get the newest (or oldest) file from a directory?](http://mywiki.wooledge.org/BashFAQ/099) – ilkkachu Nov 29 '17 at 22:26
  • Maybe this Q if you want to process one or more of the newest files? [remove oldest files](https://unix.stackexchange.com/q/35792/170373). At least take a peek at the zsh solutions. – ilkkachu Nov 29 '17 at 22:28
  • It is generally a [really bad idea](http://mywiki.wooledge.org/ParsingLs) 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](https://unix.stackexchange.com/questions/128985/why-not-parse-ls). – DopeGhoti Nov 29 '17 at 22:32
  • @ilkkachu All references suggested seems to be informative and helpful. Nevertheless I still want a hacky one-liner, assuming that its an unstable solution.. – villybyun Nov 29 '17 at 22:37
  • 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
  • The resource from @DopeGhoti says, "don't know of any implementation of ls that allows you to terminate filenames with NUL characters instead of newlines". So, there would not be a way to use NUL then. – villybyun Nov 29 '17 at 22:38
  • @ilkkachu `somecmd "$(ls -tr | tail -1)"` works. I understand that it is hacky and burns in edge cases, but still it is useful for my personal use. Thanks. – villybyun Nov 29 '17 at 23:58

2 Answers2

2

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.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
  • 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 '19 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 '19 at 16:25
  • That zsh bit `open ./*(om[1])` is something special… to the .zshrc file with you! – visyoual Jul 28 '20 at 13:06
  • @StéphaneChazelas, it does when I add it as an alias. I was simply saying i'd never seen it, and that it was a gem. I've already added it and functions beautifully. – visyoual Jul 28 '20 at 13:38
  • @visyoual. Ah OK, I'm with out now. I thought you said something along the lines of *the feature is specific to my .zshrc*. – Stéphane Chazelas Jul 28 '20 at 13:40
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
Hauke Laging
  • 88,146
  • 18
  • 125
  • 174