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

smj2393
  • 191
  • The output of ls will not return absolute path..you need to run the command from source directory.. – heemayl Apr 09 '15 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 09 '15 at 14:42
  • Please check my answer. – heemayl Apr 09 '15 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. – Scott - Слава Україні Apr 09 '15 at 18:16
  • @Gilles how is this a duplicate??? – smj2393 Apr 10 '15 at 07:57
  • Sorry, I'd only noticed the missing quotes issue, not the missing path part. Reopening. – Gilles 'SO- stop being evil' Apr 10 '15 at 08:18

4 Answers4

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.

heemayl
  • 56,300
  • If I run ls -dtr1 it outputs . It is a One not a L yes? – smj2393 Apr 09 '15 at 14:59
  • Yes, notice a * after the path (/Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/*). – heemayl Apr 09 '15 at 15:03
  • What does the * do? Sorry I am very new to this. – smj2393 Apr 09 '15 at 15:09
  • 1
    The shell will expand * to all files in that directory..Please read shell globbing to get more idea.. – heemayl Apr 09 '15 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 09 '15 at 15:20
  • But if I CD in terminal to this directory works and if I then ls it displays all the zip files. – smj2393 Apr 09 '15 at 15:20
  • As your file path contains spaces shell is doing word splitting..we need to use quote..Run cp -p "ls -dtr1 /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Original/* | tail -1" /Users/Me/Documents/Coffi\ Work/FTP\ Backup\ Shell\ Script/Backup1/ – heemayl Apr 09 '15 at 15:25
  • Check my edits on the answer.. – heemayl Apr 09 '15 at 15:26
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/
  • 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 '15 at 08: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
apaul
  • 3,378
  • This just outputs No file to copy. Both my Backup1 and Original folder have multiple zip files in. – smj2393 Apr 09 '15 at 15:07
  • Also what is the 2>/dev/null? – smj2393 Apr 09 '15 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 09 '15 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 09 '15 at 15:29
  • It doesn't work with this "${MYDIR}" either :/ I am trying to tidy the code up with variables. – smj2393 Apr 09 '15 at 15:37
  • @smj2393 yeah, dealing with spaces may be tricky (if you use double quotes, you have to let spaces un-escaped). Look at my edited script: I've removed backslashes from MY_DIR and DEST, but I've added double-quotes quite everywhere. – apaul Apr 09 '15 at 15:49
  • Yes, I have tried the same, but now it says the most recent file is a directory. When there are only Zip files in the folder? – smj2393 Apr 09 '15 at 15:54
  • I would avoid using spaces, but i'm writing a script for an old web server which has uses spaces :/ – smj2393 Apr 09 '15 at 15:54
  • Sorry Paul, I was unavailable last night. – smj2393 Apr 10 '15 at 07:55
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!
Anu
  • 111