Never been to CodeSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

About this user

jvscode [[at]] fastmail [[dot]] fm

Save & restore file permissions with xattr

Extended attributes make it possible to store (additional) file metadata on a per-file basis. This can, for example, be used to save & restore file permissions.


mkdir -p ~/Desktop/testdir
touch ~/Desktop/testdir/f{1,2,3}.txt
find -x ~/Desktop/testdir -print0 | xargs -0 stat -LF
stat -x ~/Desktop/testdir/f1.txt

which xattr   # /opt/local/bin/xattr; sudo port install xattr (after installing MacPorts, http://www.macports.org/install.php)


# store file permissions as additional Unix file metadata with xattr

find -x ~/Desktop/testdir -print0 | while read -d $'\0' filename; do
#find -x ~/Desktop/testdir -type f -or -type d -print0 | while read -d $'\0' filename; do
   fchmod="$(stat -f %p "$filename")"; \
   fchown="$(stat -f %u:%g "$filename")"; \
   xattr --set fchmod "$fchmod" "$filename"; \
   xattr --set fchown "$fchown" "$filename"; \
   #echo $fchown $fchmod; \
done


find -x ~/Desktop/testdir -print0 | xargs -0 xattr --list
find -x ~/Desktop/testdir -print0 | xargs -0 stat -LF

# change file permissions & ownership
sudo chown -R root:wheel ~/Desktop/testdir
sudo chmod -R 700 ~/Desktop/testdir

sudo find -x ~/Desktop/testdir -print0 | xargs -0 sudo stat -LF
find -x ~/Desktop/testdir -print0 | xargs -0 xattr --list
sudo find -x ~/Desktop/testdir -print0 | xargs -0 sudo xattr --list


# restore file permissions & ownership

sudo find -x ~/Desktop/testdir -print0 | while read -d $'\0' filename; do 
   fchown="$(sudo xattr --get fchown "$filename" | awk '/fchown/ {print $2}')"; \
   fchmod="$(sudo xattr --get fchmod "$filename" | awk '/fchmod/ {print $2}')"; \
   sudo chown "$fchown" "$filename"; \
   sudo chmod "$fchmod" "$filename"; \
   #echo $fchown $fchmod; \
done


find -x ~/Desktop/testdir -print0 | xargs -0 stat -LF
find -x ~/Desktop/testdir -print0 | xargs -0 xattr --list


Convert chmod file permissions between octal & symbolic notation


# convert from octal to symbolic format
unset -f o2s
function o2s () { touch ~/.octal2symbolic; chmod -vv "$@" "$_";  ls -l "$_" | awk '{print $1}'; rm -f ~/.octal2symbolic; }
#function o2s () { touch ~/.octal2symbolic; chmod "$@" "$_";  ls -l "$_" | awk '{print $1}'; rm -f ~/.octal2symbolic; }

o2s 755
o2s 644
o2s 1744
o2s 0100751      # cf. man 2 stat (st_mode)
o2s 0101751


# Cf. Bash 'umask' builtin doesn't set 'x' permissions?
# http://archives.devshed.com/forums/unix-linux-135/bash-umask-builtin-doesn-t-set-x-permissions-372356.html

UMASKO=$(umask)   # 0022

help umask
umask
umask -p
umask -S

umask 0077
umask
umask -S
o2s 755

umask 0000
umask
umask -S
o2s 654

umask $UMASKO


#---------------------------------------


# convert from symbolic to octal format

#unset -f s2o
# default file permissions of temporary file: chmod 777 ~/.symbolic2octal
#function s2o () { touch ~/.symbolic2octal; chmod 777 "$_"; chmod "$@" "$_";  stat -f %p "$_"; rm -f ~/.symbolic2octal; }


# default file permissions of temporary file ~/.symbolic2octal:  100000 or 100777 respectively

unset -f s2o
function s2o () {
   OPATH=$PATH; OIFS=$IFS
   export PATH="/usr/bin:/bin:/usr/sbin:/sbin"; export IFS=$' \t\n'
   unset -v FILE logname num str
   declare FILE="${HOME}/.symbolic2octal" logname="$(/usr/bin/logname)" str="chmod ${@} file.txt"
   declare num=$((${#str} + 5))   # calculate string length for dynamic printf spacing
   touch "${FILE}"
   chown "${logname}":"${logname}" "${FILE}"
   printf "%-${num}s %s\n" "${str}" "file permission command"
   chmod 000 "${FILE}"

   if [[ "${@}" != "${@/-/}" ]]; then    # if the string contains a minus sign ...
      chmod 777 "${FILE}"
      printf "\e[32m%-${num}s\e[m %s\n" "100777" "initial file permissions"
   else
      printf "\e[32m%-${num}s\e[m %s\n" "100000" "initial file permissions"
   fi

   chmod "$@" "${FILE}"
   printf "\e[1m%-${num}s\e[m %s\n" "$(stat -f %p "${FILE}")" "resulting file permissions"
   #stat -f %Sp "${FILE}"
   /bin/rm -f ~/.symbolic2octal
   export PATH=$OPATH; export IFS=$OIFS
   return 0
}

s2o a-w
s2o a+w
s2o u=rwx,g=rx,o=x
s2o u=rwx,g-x,o=x
s2o u=rx,g-x,o=x
s2o u=rx,g+x,o=x
s2o u=rwx,g=rx,o=x,+t


unset -f s2o
function s2o () {
   OPATH=$PATH; OIFS=$IFS
   export PATH="/usr/bin:/bin:/usr/sbin:/sbin"; export IFS=$' \t\n'
   unset -v FILE logname num str
   declare FILE="${HOME}/.symbolic2octal" logname="$(/usr/bin/logname)" str="chmod ${@} file.txt"
   declare num=30
   touch "${FILE}"
   chown "${logname}":"${logname}" "${FILE}"
   printf "%-${num}s %s\n" "file permission command:" "${str}"
   chmod 000 "${FILE}"

   if [[ "${@}" != "${@/-/}" ]]; then    # if the string contains a minus sign ...
      chmod 777 "${FILE}"
      printf "%-${num}s \e[32m%s\e[m\n" "initial file permissions:" "100777"
   else
      printf "%-${num}s \e[32m%s\e[m\n" "initial file permissions:" "100000"
   fi

   chmod "$@" "${FILE}"
   printf "%-${num}s \e[1m%s\e[m\n" "resulting file permissions:" "$(stat -f %p "${FILE}")"
   #stat -f %Sp "${FILE}"
   /bin/rm -f ~/.symbolic2octal
   export PATH=$OPATH; export IFS=$OIFS
   return 0
}


s2o a-w
s2o a+w
s2o u=rwx,g=rx,o=x
s2o u=rwx,g-x,o=x
s2o u=rx,g-x,o=x
s2o u=rx,g+x,o=x
s2o u=rwx,g=rx,o=x,+t