8

I have 3 zip files in a directory. I want to copy the most recent version to a backup directory.

My code works fine for .sh and other files, but not for .zip. Is there something else I have to do?

cp -p `ls -tr1 /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/| tail -1` /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Backup1/ 

It says: cp: website3.zip: No such file or directory

6
  • The output of ls will not return absolute path..you need to run the command from source directory..
    – heemayl
    Apr 9, 2015 at 14:36
  • Hi, sorry I am very new to Unix command line. But yes if I run the command from inside /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/ it works. How can I fix this?
    – smj2393
    Apr 9, 2015 at 14:42
  • Please check my answer.
    – heemayl
    Apr 9, 2015 at 14:48
  • This question has nothing to do with zip or zip files.  You should have done enough research (i.e., trying to solve your problem by yourself) to see that that the file extension was not the factor that determined whether the command worked.  And you should write question titles, and choose tags, that correspond better to what the question is actually about. Apr 9, 2015 at 18:16
  • @Gilles how is this a duplicate???
    – smj2393
    Apr 10, 2015 at 7:57

4 Answers 4

10

Run this:

cp -p "`ls -dtr1 "$SRC_DIR"/* | tail -1`" "$DEST_DIR"

Here, ls -d gives the absolute path.

In your command, as ls is not returning absolute paths, you must run that from the source directory to get the file copied. As you have run it from some other directory that does not have the file, the error No such file or directory being shown.

Also as you have spaces in the path we need to quote ls -dtr1 /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/* | tail -1 so that shell does not do word splitting on the output of it.

8
  • If I run ls -dtr1 it outputs . It is a One not a L yes?
    – smj2393
    Apr 9, 2015 at 14:59
  • Yes, notice a * after the path (/Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/*).
    – heemayl
    Apr 9, 2015 at 15:03
  • What does the * do? Sorry I am very new to this.
    – smj2393
    Apr 9, 2015 at 15:09
  • 1
    The shell will expand * to all files in that directory..Please read shell globbing to get more idea..
    – heemayl
    Apr 9, 2015 at 15:11
  • I get the error: cp: Work/FTP: No such file or directory cp: Backup: No such file or directory cp: Shell: No such file or directory cp: Script/Original/dfds.zip: No such file or directory
    – smj2393
    Apr 9, 2015 at 15:20
3

As others have already noticed, there are two problems with your approach. They have nothing to do with .zip vs .sh files, but with the names and locations of the files.

You need to put double quotes around command substitution. Otherwise they break at space characters. See Why does my shell script choke on whitespace or other special characters? for more details.

Furthermore the ls command outputs just the base name of the file, without the directory part. cp therefore only sees the base name and has no idea that you meant files in a different directory. One way to fix this is to switch to the desired directory first.

cd /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/
cp -p -- "`ls -tr | tail -n1`" ../Backup1/ 

If you want just the latest zip file and not the latest file whatever it is:

cd /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/
cp -p -- "`ls -dtr -- *.zip | tail -n1`" ../Backup1/ 

This will fail with a strange error message if the directory is empty or if there is a file name containing newlines or characters that are not printable in the current locale but these are relatively rare circumstances.

The option -d is a bit of additional safety in case there is a subdirectory whose name matches *.zip. You don't need the option -1, its effect is automatic when the output of ls is redirected to another command.

Alternatively, you can use zsh which makes this nice and easy thanks to its glob qualifiers.

cp -p /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/*(om[1]) /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Backup1/
1
  • Thank you Gilles. I have it working now, it takes a bit of getting used to this new syntax. I am used to HTML5 etc. Thanks though.
    – smj2393
    Apr 10, 2015 at 8:57
2

It's because it depends from where you execute your cp. In your example, ls -tr1 /Users/Me/Whatever will return only website3.zip. If you run your cp from /tmp, then it will try to find the file /tmp/website3.zip.

To let ls display the full path, you should use the wildcard *. Depending on what you want, it may be useful to specify -d option to not let ls list the content of all sub-directories:

$ ls -tr1d /Users/Me/Whatever
...
website3.zip

$ ls -tr1d /Users/Me/Whatever/*
...
/Users/Me/Whatever/website3.zip

However, be careful in case the last modified entry is a directory. In this case, cp won't copy it if you omit the -R option.

If your files always have a .zip extension you may consider:

$ ls -tr1d /Users/Me/Whatever/*.zip

You may also get an error if your directory is empty. Why don't you script something like that:

#!/bin/bash

MY_DIR="/Users/Me/Documents/Coffi Work/FTP Backup Shell Script/Original/"
DEST="/Users/Me/Documents/Coffi Work/FTP Backup Shell Script/Backup1/"
FILEEXT="zip"

NEWEST=`ls -tr1d "${MY_DIR}/"*.${FILEEXT} 2>/dev/null | tail -1`

if [ -z "${NEWEST}" ] ; then
    echo "No file to copy"
    exit 1
elif [ -d "${NEWEST}" ] ; then
    echo "The most recent entry is a directory"
    exit 1
else
    echo "Copying ${NEWEST}"
    cp -p "${NEWEST}" "${DEST}"
fi
10
  • This just outputs No file to copy. Both my Backup1 and Original folder have multiple zip files in.
    – smj2393
    Apr 9, 2015 at 15:07
  • Also what is the 2>/dev/null?
    – smj2393
    Apr 9, 2015 at 15:15
  • @smj2393 this is the same problem that you commented on @heemayl answer: your path contains spaces. The first time the space is well escaped with `, but when you got the output of ls, spaces remain un-escaped. If it's possible, avoid spaces in the name of your files or directories. If you can't, add double-quotes on the cp` line: cp -p "${NEWEST}" "${DEST}" (I made an edit)
    – apaul
    Apr 9, 2015 at 15:27
  • @smj2393 2>/dev/null redirects error message in /dev/null, so ls won't output something like "no such file or directory"
    – apaul
    Apr 9, 2015 at 15:29
  • It doesn't work with this "${MYDIR}" either :/ I am trying to tidy the code up with variables.
    – smj2393
    Apr 9, 2015 at 15:37
0
cp -p $(find `pwd` -name `ls -Art <SOURCE> | tail -1`) <DESTINATION>
  1. ls -Art <SOURCE> | tail -1 Give you the latest file in the directory
  2. $(find `pwd` -name `ls -Art <SOURCE> | tail -1`) Give you the absolute file of the latest file.
  3. cp -p $(find `pwd` -name `ls -Art <SOURCE> | tail -1`) <DESTINATION> Copies the files from source to destination!

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .