Categories
Code Linux

Run shell script as different user with proper argument handling

So you have a shell script which needs to drop or modify its privileges by switching to an appropriate system user before continuing its execution.

Here are some alternatives to accomplish this while preserving all original command line arguments properly. Privileges are dropped by switching to the nobody user, adapt the RUN_AS variable as desired.

Alternative 1 – using only su, requires being root (uid 0) or the target user to run
#!/bin/sh

# This script must run as
RUN_AS=nobody

if [ `id -nu` != $RUN_AS ]; then
    if [ `id -u` -ne 0 ]; then
        echo >&2 "Sorry, you must be either root or $RUN_AS to run me."
        exit 1
    fi

    # This environment variable is just a safe guard for endless re-exec loop
    # and something the script can use to test up to this point if it has
    # dropped privileges by re-executing itself
    if [ "$EXEC_SU" ]; then
        echo >&2 "Re-exec loop circuit breaker engaged, something is wrong"
        exit 1
    fi

    exec su $RUN_AS -s /bin/sh -c "EXEC_SU=1 \"$0\" \"\$@\"" -- "$0" "$@"
fi

# At this point, we can be sure we are running as the desired user.
echo Running as `id -nu`
for arg in "$@"; do
    echo Argument $((n=n+1)): $arg
done

This alternative requires being root or the target user when invoking the script. Command line arguments are preserved after switching.

Alternative 2 – using sudo
#!/bin/sh

# This script must run as
RUN_AS=nobody

if [ `id -nu` != $RUN_AS ]; then
    if [ -z "$SUDO_EXEC" ]; then
        exec sudo -u $RUN_AS SUDO_EXEC=1 "$0" "$@"
    else
        echo >&2 'Re-exec loop circuit breaker engaged, something is wrong'
        exit 1
    fi
fi

# At this point, we can be sure we are running as the desired user.
echo Running as `id -nu`
for arg in "$@"; do
    echo Argument $((n=n+1)): $arg
done

Unlike alternative 1, this method does not require the script to be invoked by root or the target user directly, if switching to a system user that typically has no password set on its own. (Su will also prompt for password automatically, but that requires the target user password.)

Alternative 3 – when a clean login environment is required

If you require the script to run in a clean login environment for the target user, then things become slightly more complicated. It can be accomplished by combining sudo with su:

#!/bin/sh

# This script must run as
RUN_AS=nobody

if [ `id -nu` != $RUN_AS ]; then
    if [ -z "$SUDO_SU_EXEC" ]; then
        exec sudo su -s /bin/sh - $RUN_AS -c "SUDO_SU_EXEC=1 \"$0\" \"\$@\"" -- "$0" "$@"
    else
        echo >&2 'Re-exec loop circuit breaker engaged, something is wrong'
        exit 1
    fi
fi

# At this point, we can be sure we are running as the desired user.
echo Running as `id -nu`
echo USER=$USER HOME=$HOME 
for arg in "$@"; do
    echo Argument $((n=n+1)): $arg
done

Note that since the example above switches to nobody, which is a system user that by default does not have a shell configured, we explicitly set the shell using su argument "-s /bin/sh".

Categories
Code

Simple colored git branch status in Bash prompt

Update August 14: just pushed version 1.1 today. Less code, better performance and small bugs fixed – I always like this combo.

Original post:

I just rewrote some code I’ve been using for embedding git branch status in the Bash command prompt. It should be faster, more robust, cleaner and more compatible now. So I put it on Github: https://github.com/oyvindstegard/bashgit
Update 2026: Moved to on Codeberg: https://codeberg.org/oyvindstegard/bashgit

If you use Bash and Git, give it a spin. It is intentionally kept simple in functionality, because the prompt is not my primary source of information about git repository status. I don’t want a long and cluttered prompt in general. It also tries to be as fast as possible, because no one likes a lagging command prompt.

Bashgit screenshot
Screenshot
Categories
Code Linux

New version of ds

I made some incremental improvements to my trusty ds synchronization tool. I have added ssh master connection sharing support, which should give a nice speed up in many cases. In addition to this, there are cleanups in auto completion code and more flexible command line parsing. Releases on Codeberg.

I do certain things the old fashioned way – synchronizing application config and data across different hosts is one of those areas. I don’t use Dropbox or other internet services for file sync purposes, except for the builtin Google stuff on my Android phone (which is mostly app config backup). I like to keep it peer-to-peer, simple and manual. And I like to keep my data private. Ds is just the right tool to make syncing that much easier, which I guess is the reason I wrote it.

Cloud services is not something I need to manage my data. I have my own “cloud services”: A synchronization scheme which works well enough, automated backups with regular encrypted offsite storage (a future blog post), personal web and file services and high speed fibre optic internet connection – all at home. I will admit to a certain level of NIH syndrome here, but my own solutions work well enough that I will not bother looking into something else yet.