Skip to content

Instantly share code, notes, and snippets.

@tgran2028
Last active December 30, 2023 19:38
Show Gist options
  • Save tgran2028/4f5807e22c7b60e1cda5e32aa0ea46b3 to your computer and use it in GitHub Desktop.
Save tgran2028/4f5807e22c7b60e1cda5e32aa0ea46b3 to your computer and use it in GitHub Desktop.
Util to copy file/dir from one user to another. This includes parsing relative paths to a user's home, updating ownership, and modifying file contents to home path changes (e.g. /home/foo -> /home/bar).
#!/bin/env bash
set -e
set -o pipefail
display_help() {
echo "Usage: cp-from-user [option...] --path <PATH> --source-user <SRC-USER> [--destination-user <DST-USER>]"
echo
echo "Util to copy file or directory from one user to another"
echo
echo " -p, --path <PATH> Path to the file or directory from home directory."
echo " Accepts both relative path and a relative path to"
echo " the src user's home directory"
echo " -s, --source-user <SRC-USER> Source user from which to copy the file or directory"
echo " -d, --destination-user <DST-USER> Destination user to which to copy the file or"
echo " directory. Defaults to current user"
echo " -h, --help Display help messages"
exit 0
}
cp_from_user() {
local path_rel_to_home
local src_user
local dst_user
while [ "$#" -gt 0 ]; do
case "$1" in
-p | --path)
path_rel_to_home="$2"
shift 2
;;
-s | --source-user)
src_user="$2"
shift 2
;;
-d | --destination-user)
dst_user="$2"
shift 2
;;
-h | --help)
display_help
;;
--) # end argument parsing
shift
break
;;
-*)
echo "Error: Unsupported flag $1" >&2
exit 1
;;
*) # preserve positional arguments
PARAMS="$PARAMS $1"
shift
;;
esac
done
# set positional arguments in their proper place
eval set -- "$PARAMS"
if [ -z "$path_rel_to_home" ]; then
echo "Error: path must be provided"
exit 1
fi
if [ -z "$src_user" ]; then
# check if path_rel_to_home is provided contains /root or /home/<user>
# if /root, set src_user to root
if [ "${path_rel_to_home:0:5}" == "/root" ]; then
src_user="root"
# if /home/<user>, set src_user to <user>
elif [[ "${path_rel_to_home:0:6}" == "/home/"* ]]; then
src_user="${path_rel_to_home:6}"
src_user="${src_user%%/*}"
else
echo "Error: source user must be provided"
exit 1
fi
fi
# set default value for dst_user if not provided
[ -z "$dst_user" ] && dst_user="$(whoami)"
local src_home
local src
# dst
local dst_home
local dst
# check if root or not root for $2. If not root, set src_home to /home/$2 else set src_home to /root
if [ "$src_user" != "root" ]; then
src_home="/home/$src_user"
if [ ! -d "$src_home" ]; then
echo "Error: $src_home does not exist"
exit 1
fi
else
src_home="/root"
fi
if [ "$dst_user" != "root" ]; then
dst_home="/home/$dst_user"
if [ ! -d "$dst_home" ]; then
echo "Error: $dst_user does not exist"
exit 1
fi
else
dst_home="/root"
fi
if [[ "$src_user" == "$dst_user" ]]; then
echo "source and current users are the same."
exit 1
fi
# remove src_home from path_rel_to_home
path_rel_to_home="${path_rel_to_home#"${src_home}"/}"
src="$src_home/$path_rel_to_home"
dst="$dst_home/$path_rel_to_home"
# remove // from both src and dst (possible artifact from joining vars)
src="${src//\/\//\/}"
dst="${dst//\/\//\/}"
# remove dst if exits, but with user confirmation
if [ -e "$dst" ]; then
read -p "Do you want to remove $dst? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -rf "$dst"
else
echo "Aborted"
exit 1
fi
fi
echo -e '\n-------------------------------------------------'
echo "SOURCE USER: $src_user"
echo "SOURCE FILE: '$src'"
echo "DEST. USER: $dst_user"
echo "DEST. FILE: '$dst'"
echo -e '-------------------------------------------------\n'
# check if src exist (either file or directory)
if [ ! -e "$src" ]; then
echo "Error: $src does not exist"
exit 1
fi
mkdir -p "$(dirname "$dst")" >/dev/null 2>&1
if [ -d "$src" ]; then
# if src is a directory, copy recursively
cp -rvf "$src" "$dst"
# change group and/or user ownership inline with original file
chown -R "$(stat -c "%U:%G" "$src")//$src_user/$dst_user" "$dst"
# iterate through files and ensure all old $HOME paths are replaced with the current user
for i in $(find "$dst" -type f); do
sed i "s|$src_home|$dst_home|g" "$i" > /dev/null 2>&1
done
else
# if src is a file, copy
cp -vf "$src" "$dst"
# fix ownership
chown "$(stat -c "%U:%G" "$src")//${src_user}/${dst_user}" "$dst"
# fix home path
sed i "s|$src_home|$dst_home|g" "$dst" > /dev/null 2>&1
fi
exit 0
}
### main ###
# help if no args or -h or --help is passed
if [[ $# -eq 0 ]] || [[ "$*" == *"-h"* ]] || [[ "$*" == *"--help"* ]]; then
display_help
fi
cp_from_user "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment