How do you use newgrp in a script then stay in that group when the script exits

20,671

Solution 1

newgrp adm << ANYNAME
# You can do more lines than just this.
echo This is running as group \$(id -gn)
ANYNAME

..will output:

This is running as group adm

Be careful -- Make sure you escape the '$' with a slash. The interactions are a little strange, because it expands even single-quotes before it executes the shell as the other group. so, if your primary group is 'users', and the group you're trying to use is 'adm', then:

newgrp adm << END
# You can do more lines than just this.
echo 'This is running as group $(id -gn)'
END

..will output:

This is running as group users

..because 'id -gn' was run by the current shell, then sent to the one running as adm. Anyways, I know this post is ancient, but hope this is useful to someone.

Solution 2

The following works nicely; put the following bit at the top of the (Bourne or Bash) script:

### first become another group
group=admin

if [ $(id -gn) != $group ]; then
  exec sg $group "$0 $*"
fi

### now continue with rest of the script

This works fine on Linuxen. One caveat: arguments containing spaces are broken apart. I suggest you use the env arg1='value 1' arg2='value 2' script.sh construct to pass them in (I couldn't get it to work with $@ for some reason)

Solution 3

The newgrp command can only meaningfully be used from an interactive shell, AFAICT. In fact, I gave up on it about ... well, let's say long enough ago that the replacement I wrote is now eligible to vote in both the UK and the USA.

Note that newgrp is a special command 'built into' the shell. Strictly, it is a command that is external to the shell, but the shell has built-in knowledge about how to handle it. The shell actually exec's the program, so you get a new shell immediately afterwards. It is also a setuid root program. On Solaris, at least, newgrp also seems to ignore the SHELL environment variable.

I have a variety of programs that work around the issue that newgrp was intended to address. Remember, the command pre-dates the ability of users to belong to multiple groups at once (see the Version 7 Unix Manuals). Since newgrp does not provide a mechanism to execute commands after it executes, unlike su or sudo, I wrote a program newgid which, like newgrp, is a setuid root program and allows you to switch from one group to another. It is fairly simple - just main() plus a set of standardized error reporting functions used. Contact me (first dot last at gmail dot com) for the source. I also have a much more dangerous command called 'asroot' that allows me (but only me - under the default compilation) to tweak user and group lists much more thoroughly.

asroot: Configured for use by jleffler only
Usage: asroot [-hnpxzV] [<uid controls>] [<gid controls>] [-m umask] [--] command [arguments]
    <uid controls> = [-u usr|-U uid] [-s euser|-S euid][-i user]
    <gid controls> = [-C] [-g grp|-G gid] [-a grp][-A gid] [-r egrp|-R egid]
Use -h for more help

Option summary:
 -a group  Add auxilliary group (by name)
 -A gid    Add auxilliary group (by number)
 -C        Cancel all auxilliary groups
 -g group  Run with specified real GID (by name)
 -G gid    Run with specified real GID (by number)
 -h        Print this message and exit
 -i        Initialize UID and GIDs as if for user (by name or number)
 -m umask  Set umask to given value
 -n        Do not run program

 -p        Print privileges to be set
 -r euser  Run with specified effective UID (by name)
 -R euid   Run with specified effective UID (by number)
 -s egroup Run with specified effective GID (by name)
 -S egid   Run with specified effective GID (by number)
 -u user   Run with specified real UID (by name)
 -U uid    Run with specified real UID (by number)
 -V        Print version and exit
 -x        Trace commands that are executed
 -z        Do not verify the UID/GID numbers
Mnemonic for effective UID/GID:
    s is second letter of user;
    r is second letter of group

(This program grew: were I redoing it from scratch, I would accept user ID or user name without requiring different option letters; ditto for group ID or group name.)

It can be tricky to get permission to install setuid root programs. There are some workarounds available now because of the multi-group facilities. One technique that may work is to set the setgid bit on the directories where you want the files created. This means that regardless of who creates the file, the file will belong to the group that owns the directory. This often achieves the effect you need - though I know of few people who consistently use this.

Solution 4

This example was expanded from plinjzaad's answer; it handles a command line which contains quoted parameters that contain spaces.

#!/bin/bash
group=wg-sierra-admin
if [ $(id -gn) != $group ]
then
    # Construct an array which quotes all the command-line parameters.
    arr=("${@/#/\"}")
    arr=("${arr[*]/%/\"}")
    exec sg $group "$0 ${arr[@]}"
fi

### now continue with rest of the script
# This is a simple test to show that it works.
echo "group: $(id -gn)"
# Show all command line parameters.
for i in $(seq 1 $#)
do
    eval echo "$i:\${$i}"
done

I used this to demonstrate that it works.

% ./sg.test  'a b' 'c d e' f 'g h' 'i j k' 'l m' 'n o' p q r s t 'u v' 'w x y z'
group: wg-sierra-admin
1:a b
2:c d e
3:f
4:g h
5:i j k
6:l m
7:n o
8:p
9:q
10:r
11:s
12:t
13:u v
14:w x y z
Share:
20,671
Paul
Author by

Paul

I am a Manager of Embedded Firmware at Enphase Energy. I currently live and work in Austin Texas

Updated on July 09, 2022

Comments

  • Paul
    Paul almost 2 years

    I am running a script on a solaris Box. specifically SunOS 5.7. I am not root. I am trying to execute a script similar to the following:

    newgrp thegroup << FOO
    source .login_stuff
    echo "hello world"
    FOO

    The Script runs. The problem is it returns back to the calling process which puts me in the old group with the source .login_stuff not being sourced. I understand this behavior. What I am looking for is a way to stay in the sub shell. Now I know I could put an xterm& (see below) in the script and that would do it, but having a new xterm is undesirable.

    Passing your current pid as a parameter.

    newgrp thegroup << FOO
    source .login_stuff
    xterm&
    echo $1
    kill -9 $1
    FOO

    I do not have sg available. Also, newgrp is necessary.

  • Paul
    Paul over 15 years
    I think that would work. I will email you and ask you for the source (or my email is [email protected]) Can I use the source with no restrictions. I ask this because it is for commercial use (though in house use) This answer is the excepted answer.
  • Martin Carpenter
    Martin Carpenter over 15 years
    Scorching answer! Solaris 10's newgrp(1) (at least) preserves the user's shell, and does indeed check $SHELL (I checked OpenSolaris source).
  • Yauhen Yakimovich
    Yauhen Yakimovich almost 12 years
    Can quotes help? as in exec sg nas_bio3 "$@"
  • Jolta
    Jolta almost 10 years
    I don't know why an answer for Linuxen was accepted when the question was tagged Solaris. I'm in the same situation, and "sg" doesn't exist on my Solaris box.
  • Shardj
    Shardj over 5 years
    This straight up doesn't do anything for me. But I am on ubuntu, could you explain what << is supposed to do, what ANYNAME is supposed to represent, same for END.
  • P.V.
    P.V. about 4 years
    This doesn't seem to answer the question, as you still won't be in the group adm when the script is exited.
  • jxmallett
    jxmallett about 3 years
    @Shardj I'm on Ubuntu 20.04 and this is working for me. ANYNAME and END are heredocs - They basically allow for easy creation of multi-line strings, I believe.