Identify and assign most recent file to shell variable

24,484

Solution 1

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).

Solution 2

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.

Share:
24,484

Related videos on Youtube

Levon
Author by

Levon

#SOreadytohelp I am here to share what I know and to learn from those more experienced without the need to be rude or put down those who come to learn. I will never downvote a solution that is functional & answers OP's question (even if there are better solutions). I will upvote good solutions and questions. I may downvote incorrect solutions if they clearly don't solve OP's problem but always with a reason, how else can anyone benefit? Downvotes without explanation help no one (OP, SO or me). Pointing out errors/making suggestions allows for improving/correcting answers which benefits everyone on SO. I don't care for aggressive self-righteous know-it-all types. I wasn't born with all that I know now, and neither were you.

Updated on September 18, 2022

Comments

  • Levon
    Levon over 1 year

    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
    
  • Levon
    Levon over 10 years
    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
    Levon over 10 years
    Thanks - I do know the filenames, so this won't be an issue. What is the advantage of using double-quotes around the variable?
  • Stéphane Chazelas
    Stéphane Chazelas over 10 years
    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).
  • Stéphane Chazelas
    Stéphane Chazelas over 10 years
    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)
  • Stéphane Chazelas
    Stéphane Chazelas over 10 years
    @Levon, answer updated with details.
  • slm
    slm over 10 years
    @Levon - double quotes protects the mv command in cases where there are spaces within the file names.
  • Levon
    Levon over 10 years
    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
    Levon over 10 years
    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
    Levon over 10 years
    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
  • hajikelist
    hajikelist over 6 years
    instead of "ls -t" you could perhaps use "find . -maxdepth 1 -type f | head -n1" to limit to regular files...
  • Stéphane Chazelas
    Stéphane Chazelas almost 4 years
    @hajikelist, that would not sort the list by modification time.