yotka
yotka

Reputation: 1043

Shell script: Get name of last file in a folder by alphabetical order

I have a folder with backups from a MySQL database that are created automatically. Their name consists of the date the backup was made, like so:

2010-06-12_19-45-05.mysql.gz  
2010-06-14_19-45-05.mysql.gz  
2010-06-18_19-45-05.mysql.gz  
2010-07-01_19-45-05.mysql.gz

What is a way to get the filename of the last file in the list, i.e. of the one which in alphabetical order comes last?

In a shell script, I would like to do something like

LAST_BACKUP_FILE= ???
gunzip $LAST_BACKUP_FILE;

Upvotes: 21

Views: 15164

Answers (4)

Mark H
Mark H

Reputation: 617

ls can yield unexpected results when parsed by other commands if the filenames have unusual characters. The following always works:

for LAST_BACKUP_FILE in *; do : ; done

for LAST_BACKUP_FILE in * loops through every filename (and folder name, if there are any) in order in the current directory, storing each in $LAST_BACKUP_FILE

do : does nothing

done finishes after the last file

Now, the last file is stored in $LAST_BACKUP_FILE.

If you happen to want the first file, use this:

for FIRST_BACKUP_FILE in *; do break; done

The break statement jumps out of the loop after the first file is stored in $FIRST_BACKUPT_FILE.

(from comment below) If you want hidden files included in the search, then use the command shopt -s dotglob before running the loops.

Upvotes: 8

Jens
Jens

Reputation: 72687

The shell is more powerful than many think. Just let it work for you. Assuming filenames without spaces,

set -- $(ls -r *.gz)
LAST_BACKUP_FILE=$1

does the trick with a single fork, no pipes, and you can even avoid the fork if your shell supports arithmetic expansion as in

set -- *.gz
shift $(($# - 1))
LAST_BACKUP_FILE=$1

Upvotes: 2

just somebody
just somebody

Reputation: 19247

@Sjoerd's answer is correct, I'll just pick a few nits from it:

  1. you don't need the -1 option to enforce one path per line if you pipe the output somewhere:

    ls | tail -n 1
    
  2. you can use -r to get the listing in reverse order, and take the first one:

    ls -r | head -n 1
    
  3. gunzip some.log.gz will write uncompressed data into some.log and remove some.log.gz, which may or may not be what you want (probably isn't). if you want to keep the compressed source, pipe it into gunzip:

    gunzip < some.file.gz
    
  4. you might want to protect the script against situation when the dir contains no files, since

    gunzip $empty_variable
    

expands to just

    gunzip

and such invocation will wait indefinitely for data on standard input:

    latest="$(ls -r /some/where/*.gz | head -1)"
    if test -z "$latest"; then
      # there's no logs yet, bail out
      exit
    fi
    gunzip < $latest

Upvotes: 11

Sjoerd
Sjoerd

Reputation: 75619

ls -1 | tail -n 1

If you want to assign this to a variable, use $(...) or backticks.

FILE=`ls -1 | tail -n 1`
FILE=$(ls -1 | tail -n 1)

Upvotes: 24

Related Questions