How to copy a folder preserving attributes and structure using 'cp'

I need to copy a selection of files and folders preserving their attributes and structure.
I want to use ‘cp’ but, can’t get it to copy the folders themselves. This is a test:

tell application "Finder"
	set sourceItemPath to (path to documents folder as text) & "zzzz:" -- 'zzzz:' is just a test folder with some contents.
	set targetFolderPath to path to desktop
end tell
tell current application
	do shell script "cp -pR " & quoted form of POSIX path of sourceItemPath & " " & quoted form of POSIX path of targetFolderPath
	--> copies ONLY the contents of  'zzzz:' but, NOT the folder itself.
end tell

It does actually do what the the man says: if the source ends with ‘/’ then it copies only the contents. But, how do I tell ‘cp’ to copy the folder itself?

I know that in AS I can strip the end ‘/’ by testing the item for kind and, if it is a folder, use a construct like this:

tell application "Finder"
	set sourceItemPath to alias ((path to documents folder as text) & "zzzz:")
	set sourceItemPath to (container of sourceItemPath as text) & name of sourceItemPath
	set targetFolderPath to path to desktop
end tell
tell current application
	do shell script "cp -pR " & quoted form of POSIX path of sourceItemPath & " " & quoted form of POSIX path of targetFolderPath
	--> copies the folder itself, complete with all contents, preserving all atributes and folder structure.
end tell

but, isn’t there a ‘cp’ syntax to copy a folder complete with folder structure and attributes?

Hello.

I’d have a look at ditto, and rsync, if I were you, cp for that case as well, in a Terminal window, using the man command, so you type “man rsync” for instance, in order to read what the command does, and don’t.

By the way, I have a gman shell-script floating around here, that presents the man page in quicklook, you need to put it into a folder that is in your PATH variable, you also need to change it into an executable, once you have saved it into a folder.

[code]#!/bin/bash
argc=$#
if [ $argc -lt 1 ] ; then
echo “gman takes a man page, if found and formats it into html.”
echo “Usage: gman [-s1…n|-s 1…n] manfile”
exit 2
fi
a=man -aw $* 2>/dev/null|head -1 2>&1 >/dev/null
if test x$a = x ; then
if [ $argc -eq 1 ] ; then
echo “gman: Can’t find $1”
exit 1
elif [ $argc -eq 2 ] ; then
echo “gman: Can’t find $2 in section ${1#-s}, try another section.”
exit 1
elif [ $argc -eq 3 ] ; then
echo “gman: Can’t find $3 in section $2, try another section.”
exit 1
else
echo “gman: “$*” didn’t work for me.”
exit 1
fi
fi

Figures out if it is a normal man page or something else (gz).

b=man -aw $* |head -1 |grep "gz" 2>&1 >/dev/null
t=mktemp -t gman.1.XXXXXXXXXX
if test x$b = x ; then
groff -mandoc $a -Thtml >|$t.html 2>/dev/null
else
gzcat -f $b |groff -mandoc -Thtml >|$t.html 2>/dev/null
fi
echo |qlmanage -px $t.html 2&>1 >/dev/null &[/code]

Hi.

You don’t need to strip the trailing slash, just don’t add one in the first place. :slight_smile:

-- Don't use a Finder 'tell' statement. It's not needed here.

set sourceItemPath to (path to documents folder as text) & "zzzz" -- Leave off the trailing colon.
set targetFolderPath to path to desktop

do shell script "cp -pR " & quoted form of POSIX path of sourceItemPath & " " & quoted form of POSIX path of targetFolderPath
--> copies the folder itself, complete with all contents, preserving all atributes and folder structure.

Hi McUsrII, thanks to this forum, your’s included, I have quite a few ways to access and read man pages. And do so.
I’m updating older scripts, which used ‘duplicate’ and ‘move’. I use ‘mv’ to move between volumes as well.
I want to replace ‘duplicate’ with ‘cp’ to copy both, files and folders in one go.
Before attempting to use ‘ditto’ or ‘rsync’, i’d like to know if there is a syntax for ‘cp’ to copy a folder preserving its structure. My reading of the ‘cp’ man page didn’t help me. Is it possible?

Sorry Nigel, I should have noted that this is part of a larger script and the folders come with trailing ‘:’ or ‘/’. They are needed elsewhere in the script, so I do have to strip them somewhere. And because in my real script, this is the only place where I need the stripped form, is there a way to strip them within the do shell script command?

Hello.

The man pages, that comes with the system, is the one that reflects what the commands does.

Should you ever wonder about what command does what, you can type apropos, or man -k keyword.

If you are in doubt whether you are looking at the correct man page or not, you can type man -aw
, and you’ll see all man pages listed in the order they appear. (The upper one, is the one that would normally be displayed.)

I’ll just paste what I found about recursion, by applying the command man /usr/share/man//man1/cp.1.gz
(after using man -aw cp).

You can read the rest for yourself, I do however believe that either cp -pR or cp -a will do the trick for you, the first includes symbolic links, the second one ignores them.

:smiley: Edit

I have some more rants about manpages while I am at it: manpages seems to fall between two schools, and maybe a third that ignores that the two exists.

The first school, tries to keep the man pages as up to date as possible, in order to reflect commands.

The second school, doesn’t update manpages, but relies on info documents, to be viewed with the info reader in a terminal window. Many of the commands that are on your machine, ancestries from GNU, and is of this kind.

The third school, would be Apple, that neglects the fact that(or at least neglected) GNU’s approach to man pages, and haven’t delivered all of the info files. So, if you see a reference to an info file in a man page, and you can’t find the man page, then you should take a trip over to GNU and acquire it!

Thanks again of for advise about reading. I did and do.
If you read my initial first script example, you’ll see that ‘cp -pR’ doesn’t do the trick (and ‘cp -a’ wouldn’t do it either) :confused:
They require a folder without trailing ‘:’ or ‘/’.
My second example does the stripping before the do shell script and then ‘cp -pR’ does works.
As I said in my reply to Nigel, the folders come with trailing ‘:’ or ‘/’ so, I’d like to know how to strip them within the do shell script (unless it is better to do it before, as I did ;)).


if last character of sourceItemPath is in {":", "/"} then set sourceItemPath to text 1 thru -2 of (get sourceItemPath)

Hello.

If you have stripped off the ending “/” I can’t understand otherwise than that cp -pR should do what you want to do, if you want to copy every file and folder, preserving the files and folders attributes, and even tossing in whatever have been symlinked into the three, with attributes preserved for good measure.

which -a cp

will show you what cp command you would effectively execute when not giving a full path to it. The path to a man page, and the path to a command will reflect upon each other; commands under /usr/bin will have their man pages under /usr/share/man, and so on.

Sometimes one has to choose the command from the man-page one likes. :wink:

Thanks Stefan,
I’ll add that before my do shell script "cp -pR " line :slight_smile:

Hello.

I forgot to mention, that it can also be indulging to execute a “which -a command” from a do shell script, as the reality may vary between a terminal window, and a do shell script, if you have added/installed commands, and/or tampered with the path on your own.

And we always do dryrun our commands from a terminal window, before we implement them in do shell scripts, especially if the result is undoable. But should something ugly happen, then it is cool to kow that you have it all on the TimeMachine! :slight_smile:

Hi, I’m afraid that we a talking a bit cross purpose. As you can see in my initial post, I did the stripping but, I wanted to know how I could do it within the do shell script. Anyway, mine or better Stefan’s way will do it before the shell command.

Hello.

Here is a way to do it within a do shell script. Select a folder in finder, and run the script so you’ll see in the results pane, what the bash brace substitution does!

tell application "Finder"
	if selection = {} then
		beep
		return
	end if
	set n to (count selection as list)
	if n > 1 then
		beep
		return
	else
		set a to POSIX path of (selection as alias)
	end if
end tell
set b to (do shell script "a=\"" & a & "\";echo \"${a%%/}\"")
b
set sourceItemPath to (path to documents folder as text) & "Garvey Family Tree:" as alias
set targetFolderPath to path to desktop

do shell script " src=$(<<<" & quoted form of POSIX path of sourceItemPath & " sed 's/[:/]$//') ; cp -pR \"$src\" " & quoted form of POSIX path of targetFolderPath

Edit: cp input made space-proof.

Many thanks for the two shell solutions :slight_smile: