159

Is there a command or flag to clone the user/group ownership and permissions on a file from another file? To make the perms and ownership exactly those of another file?

1

8 Answers 8

224

On GNU/Linux chown and chmod have a --reference option

chown --reference=otherfile thisfile
chmod --reference=otherfile thisfile
3
  • 1
    Could you reference to this answer (and likely cite it) as answer to my question : unix.stackexchange.com/questions/44253/… ? , I think I will be great addition and I'd love to find up-votes there for it. Commented Jul 31, 2012 at 20:40
  • @GrzegorzWierzowiecki: probably that question should be closed, but is a little bit different than this and already has answers, so I better do nothing.
    – enzotib
    Commented Jul 31, 2012 at 20:54
  • As you wish and suggest. Thanks for help, I have never put attention to --reference parameter of chmod and chown before :). Commented Jul 31, 2012 at 22:02
13

On any unix with GNU utilities, such as (non-embedded) Linux or Cygwin, you can use chmod --reference and chown --reference.

If your system has ACLs, try the ACL commands getfacl and setfacl. These commands differ a little from system to system, but on many you can use getfacl other_file | setfacl -bnM - file_to_change to copy the permissions. This doesn't copy the ownership; you can do that with careful parsing of ls -l other_file, assuming that you don't have user or group names containing whitespace.

LC_ALL=C ls -l other_file | {
  read -r permissions links user group stuff;
  chown -- "$user:$group" file_to_change
}
getfacl other_file | setfacl -bnM - file_to_change
3
  • 1
    You should have ACL installed and filesystem mounted with ACL enabled.
    – enzotib
    Commented Sep 14, 2011 at 4:03
  • 2
    @enzotib At least on Linux, ACL tools will work to copy permissions (but not ownership) even if the source and target filesystem don't support ACLs. Commented Sep 14, 2011 at 6:57
  • Pour info, on RHEL 7 (and possibly onwards) you can use getfacl other_file | setfacl --set-file=- file_to_change.
    – dr_
    Commented Mar 29, 2023 at 14:01
10

Did a bash command based on the response of Matteo :)

Code:

chmod $( stat -f '%p' "$1" ) "${@:2}"

Usage:

cp-permissions <from> <to>...

2
  • 9
    Egad!  Where did you learn to say ${*:2}?  Don't ever do that again!  That will fail if any of the filenames contain space (or tabs).  Use "${@:2}".  Also, use "$1" instead of just $1. Commented May 26, 2015 at 4:10
  • chmod "$(stat -c '%a' "$fromfile")" tofile in GNU Coreutils, but you might as well use --reference in that case since thestat CLI utility is not POSIX, it even says pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.htmlthat ls -l won't cut it: "The output of ls (with the -l and related options) contains information that logically could be used by utilities such as chmod and touch to restore files to a known state. However, this information is presented in a format that cannot be used directly by those utilities or be easily translated into a format that can be used." Commented Sep 2, 2018 at 12:10
4

If you are not using a system with GNU's chmod/chown (which support the --reference option) you could try to parse the output of ls -l

Here a small script for chmod (if you have a see which supports extended regexes they could be written in a much more readable way ...)

#!/bin/sh

reference=$1
shift
files=$*

# strip the permissions (whith extended regexes could be more readable)
OWNER=$(ls -l ${reference} | sed -e "s/.\(...\).*/\1/"       | sed -e "s/[-]//g" )
GROUP=$(ls -l ${reference} | sed -e "s/....\(...\).*/\1/"    | sed -e "s/[-]//g" )
OTHER=$(ls -l ${reference} | sed -e "s/.......\(...\).*/\1/" | sed -e "s/[-]//g" )

chmod u=${OWNER},g=${GROUP},o=${OTHER} ${files}

UPDATE:

This is even easier using stat:

chmod $( stat -f '%p' ${reference} ) ${files}
3
  • 2
    Instead of parsing ls -l output, you could you could parse statoutput.
    – jfg956
    Commented Sep 21, 2011 at 19:51
  • @jfgagne: thanks makes sense I do not know why I didn't think about it in the first place. I updated the answer
    – Matteo
    Commented Sep 22, 2011 at 5:28
  • 2
    You're using *BSD stat syntax here. Your chmod $(stat ...) command won't work because %p alone outputs too much information for *BSD's chmod, use %Lp to output just the u/g/o bits. Something slightly more elaborate would be required for sticky/setuid/setgid bits. Commented Jun 7, 2013 at 10:17
2

This works for me:

cp -p --attributes-only <from> <to>

1
  • 1
    That copies all attributes though (change -p to --preserve=ownership,mode to copy only permissions and ownership (and ACLs if any)). That will also not work for files of type directory. Also note that if <to> is a symlink, that will copy the attributes to the target of the symlink (likely what one would want anyway as permissions of symlinks themselves are rarely relevant (though ownership can be)). Commented Jan 27, 2022 at 16:46
2

On MacOS, cp --attributes-only or chmod --reference won't work.

A solution for MacOS is to first install coreutils using

brew install coreutils

then use the coreutils's version of cp command, that is gcp:

gcp --attributes-only --archive  sourcefile destfile

This will copy the ownership and attributes while preserving destfile's content and filename.

1
  • note that --archive works recursively, which is not necessarily what one may want. Commented Sep 2, 2022 at 17:19
0

I wanted to add an adjustment to Matteo's script. A for loop should be used to validate that the files exist before actually running the chmod command on them. This will let the script error out more gracefully.

I think this is the best option because it can be used for all *nix OSes, like Solaris, Linux, etc.

#!/bin/sh

reference=$1
shift
files=$*

for file in $reference $files; do
  [ -f $file ] || { echo "$file does not exist"; exit 1; }
done

# strip the permissions (whith extended regexes could be more readable)
OWNER=$(ls -l ${reference} | sed -e "s/.\(...\).*/\1/" | sed -e "s/[-]//g" )
GROUP=$(ls -l ${reference} | sed -e "s/....\(...\).*/\1/" | sed -e "s/[-]//g" )
OTHER=$(ls -l ${reference} | sed -e "s/.......\(...\).*/\1/" | sed -e "s/[-]//g" )

chmod u=${OWNER},g=${GROUP},o=${OTHER} ${files}

I found that on one of my Solaris 10 machines, stat was not found. That might be an issue with my configuration though.

1
0

I got inspired by @Matteo and @mjlescano. This one-liner works on MacOS without installing any additional dependencies.

permcp() { chmod $( stat -f '%A' "$1" ) "${@:2}" } && permcp sourcefile targetfile

We have just defined a function and called the function with source and target file parameters. You can use the "permcp" function for subsequent invocations if it's required.

You must log in to answer this question.

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