8

I have a directory of .jpg files that continuously grows. I want to copy the most recent one elsewhere. This is what I currently have and it works, just curious if there's a better way to identify the most recent jpg file rather than using ls and tail.

#!/bin/bash
cd /home/pi/JPGS
fn=$(ls -rt1 | tail -1)
mv -f $fn /home/pi/WWW/webpic.jpg

4 Answers 4

14

There's a number of characters in file names that would make that fail. You can improve it with:

#! /bin/sh -
cd /home/pi/JPGS || exit
fn=$(ls -t | head -n1)
mv -f -- "$fn" /home/pi/WWW/webpic.jpg

Leaving a variable unquoted in list context (in Bourne-like shells other than zsh) is the split+glob operator, you almost never want to do that. -- marks the end of the options so "$fn" will not be taken as an option if it starts with -.

That still fails if filenames contain newline characters, but not space, tab, star, question mark, right square bracket, or start with dash.

Best is to use zsh here:

#! /bin/zsh -
mv -f /home/pi/JPGS/*.jpg(.om[1]) /home/pi/WWW/webpic.jpg

(.om[1]) are glob qualifiers, they are a zsh specific feature. . restricts the glob to regular files (will not include symlinks, directories, devices...), om is to order on modification time, and [1] to only take the first file.

Note that if you want to assign that to a shell variable, that would have to be an array variable:

fn=(/home/pi/JPGS/*.jpg(.om[1]))

(not that it makes a lot of difference on how you use it later).

7
  • Thanks - I have a few questions: What's the significance of the -- and why is zsh preferable over bash here? I'm also not sure (.om[1]) does.
    – Levon
    Commented Dec 21, 2013 at 21:57
  • 1
    @Levon, answer updated with details. Commented Dec 21, 2013 at 22:21
  • Thanks .. good to store this away for future possible use, my filenames are quite regular so I won't have to worry about special cases for this.
    – Levon
    Commented Dec 22, 2013 at 0:24
  • 1
    @Levon, that's no reason (IMO) to use the wrong syntax. You should really get used to not writing things like mv $f xx which is the wrong syntax and bad practice (and where most of the shell-related vulnerabilities come from) but mv -- "$f" xx Commented Dec 22, 2013 at 13:58
  • You are right of course, my comment was with regard to the globing, not re -- which is clearly important from a security standpoint too - thanks again
    – Levon
    Commented Dec 22, 2013 at 14:13
8

Listing file

You could reverse the logic on the ls a bit.

$ ls -t | head -n1

Details

   -t     sort by modification time, newest first

Now it shows up first so we can use head to return the first result.

NOTE: You could also sort the list by change time (ctime), though you're probably going to want to use modify time above - (mtime). The ctime is the last time the file status meta information was changed.

   -c     with -lt: sort by, and show, ctime (time of last modification of 
          file status information) with -l: show ctime and sort by name
          otherwise: sort by ctime, newest first

For example:

$ ls -tc | head -n1

Moving the file

To do the move more cleanly you'll want to wrap the filename in double quotes.

Example

$ mv -f -- "$fn" /home/pi/WWW/webpic.jpg

This will work in the majority of cases, there are a handful of legal filenames where it won't, for example, files with new lines. But these, though legal, are rarely ever intentionally used.

5
  • Thanks - I do know the filenames, so this won't be an issue. What is the advantage of using double-quotes around the variable?
    – Levon
    Commented Dec 21, 2013 at 21:58
  • he'll probably want to sort on mtime (which reflects the modification time of the content of the file) rather than ctime (which reflects the modification time of metadata as well). Commented Dec 21, 2013 at 22:05
  • Note that, portably (and as the documentation you quote mentions), you need the -t option as well: ls -tc to sort on the inode-change time (and ls -lc to display the inode-change time, and ls -ut to sort on access time) Commented Dec 21, 2013 at 22:20
  • 1
    @Levon - double quotes protects the mv command in cases where there are spaces within the file names.
    – slm
    Commented Dec 21, 2013 at 22:57
  • 1
    Thanks, very useful info re the use of "" .. my current filenames won't have that problem, but I will start using this as the default way of doing things.
    – Levon
    Commented Dec 22, 2013 at 0:25
0

To assign the most recent file with a jpg suffix, wherever it is, to a shell variable:

fn=$( locate .jpg | xargs ls -rt | tail -1)

Thanks to locate, this is quick even if you have a large number of files. Main drawback: files modified since the last update of the locate database are not taken into account.

Another drawback: does not behave well when there is no file with .jpg (because of ls).

0

I guess I'd use find as it is more general. I just ran into a variant of this, determining the newest executable but it comes up in other places. The find command,

find .. -type f -printf "%T@ %p\n" | sort -g

should be quite flexible and work with any file names. Change the path and criteria as you need and then tail the output.

Although I admit for my case I ended up with,

EXIF=`ls -t \`which -a exiftool \` | awk '{print $1}' `


which -a setup
/home/scripts/setup
/home/scripts/lat/scripts/setup
/home/documents/latex/scripts/setup
marchywka@happy$ ls -t `which -a setup `
/home/scripts/setup /home/documents/latex/scripts/setup /home/scripts/lat/scripts/setup
marchywka@happy$ ls -alt `ls -t \`which -a setup \` `
-rwxrwxrwx 1 root root 897 Oct  8 17:54 /home/scripts/setup
-rwxrwxrwx 1 root root 166 Sep 13 13:50 
/home/scripts/lat/scripts/setup
-rwxrwxrwx 1 root root 163 Jan 27  2019 /home/documents/latex/scripts/setup

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.