I needed to do this in a shell script (script adds current user to a group, runs later commands that require that group membership). The newgrp
command is brittle in that it can only run a new shell rather than an arbitrary command (I want to re-run the current shell script with original commandline args).
Here's my solution, which uses lots of bash-isms: (note the surrounding script needs some sort of branch to only run this function if the required group is not currently active):
(note: the script implies that it ran sudo adduser $user docker
, which means it could also just run sudo docker
everywhere instead of docker
but that was undesirable in this case)
# save these for later
ORIGINAL_ARGS=("$@")
# This function is a little insane. The problem is this: user running
# this script is not a member of docker group, but used 'sudo' to add
# themselves to group. Without logging out and back in, the only way
# to gain access to that group is via the 'newgrp' command, which
# unfortunately starts a new shell rather than an arbitrary command...
#
# Also, we're going to newgrp twice: first to add the new group and
# again to restore the original group (but the new group remains in
# the 'groups' output).
#
# So this horrendous hack dups stdin to fd3 for later. Then runs
# 'newgrp' piping in a script that runs 'newgrp' a second time, piping
# in another script that restores stdin from fd3 and execs the
# original command...
restart-newgrp-newgrp() {
# dup original stdin to fd3
exec 3<&0
local group="$1"
local command="$0"
local arg
for arg in "${ORIGINAL_ARGS[@]}"; do
printf -v command "%s %q" "$command" "$arg"
done
# restore original stdin from fd3; also replace any single-quote in
# command with '"'"' to embed it in a single-quoted string
local script
printf -v script "exec newgrp %q <<<'exec <&3-; exec %s'" "$(id -gn)" "${command//\'/\'\"\'\"\'}"
exec newgrp "$group" <<<"$script"
}