bash execute commands as different user with su - bash

I have the most simple issue and I am so not certain what I am doing wrong.
I have a simple shell script using /bin/sh
inside the script I have the following:
exec_as_wwwdata() {
if [ $(whoami) = ${WWW_USER} ]]; then
$#
else
su -s /bin/sh -c '$#' ${WWW_USER}
fi
}
I am calling it with
exec_as_wwwdata composer config -g github-oauth.github.com $COMPOSER_GITHUB_TOKEN
it just does nothing, no error message nothing.
If I call the following directly inside the script
su -s /bin/sh -c 'composer config -g github-oauth.github.com $COMPOSER_GITHUB_TOKEN' ${WWW_USER}
it works.
What am I doing wrong here?
Based on feedback I have changed it to this
exec_as_wwwdata() {
if [ $(whoami) = ${WWW_USER} ]]; then
$#
else
su -s /bin/sh -c '"$#"' "$WWW_USER" _ "$#"
fi
}
Although when I am calling it with the following arguments
exec_as_wwwdata /usr/local/bin/php /usr/local/bin/composer create-project --repository-url=xxxx .
I receive the following error message
su: unrecognized option '--repository-url=
I think there is issue with -- in the string. How can I escape that ?

There are two overlapping uses of $# here, and you've inadvertently stumbled on a partial correct solution. -c expects a single word, while "$#" would produce multiple distinct words. The correct solution would be
su -s /bin/sh -c '"$#"' "$WWW_USER" _ "$#"
The short version: you don't want to build a command string from the current parameters; you want to pass them as arguments to a hard-coded command string to let the new shell expand things appropriately.
A breakdown:
-s /bin/sh - use /bin/sh instead of the appropriate users's login shell
-c '"$#"' run the command "$#", as desired. Note this is a hard-coded value; the new shell will expand its positional parameters correctly once it has started.
"$WWW_USER" - specify the user to run the shell as
_ - specify the value of $0 in the shell being run. You probably don't care what this value is; you just need some placeholder to prevent your first real argument from being treated as the value for $0.
"$#" pass the current positional parameters as arguments to the new shell, which will expand its "$#" to these values.

Related

tsp fails to run the bash command

I'm trying to use ts/tsp to schedule idle tasks that I need done from time to time, which is OK if they don't complete due to a crash.
So far, I'm trying with a script like this:
the_args=(--long-arg /usr/share/lib --long-arg2 -j $j -o "'$path_o/'" -i "'$path_i'")
tsp -m -L "$jobname" bash -c '
echo task "$#"
cgexec -g cpu,freezer:execting exector "$#"
' "${the_args[#]}"
I want to run executor with the args given by the_args
I've tried many alternatives, including:
tsp -m -L "$jobname" bash -c "
echo task ${the_args[#]}
cgexec -g cpu,freezer:execting exector ${the_args[#]}
"
I've also tried with heredocs with different configurations.... None of them worked.
Unfortunately, none of those allow me to call the command with all the args. Some methods pass only the first element in the list (the ones shown), others don't even work.
What is the correct way to pass parameters to inside the deferred script?
The first argument following -c's argument is used to set $0 in the shell; it is not included in $#. You need to provide some dummy argument (since you probably don't care what $0 actually is).
Your quoting inside the_args also needs to be simpilfied.
the_args=(
--long-arg /usr/share/lib
--long-arg2
-j "$j"
-o "$path_o/"
-i "$path_i"
)
tsp -m -L "$jobname" bash -c '
echo task "$#"
cgexec -g cpu,freezer:execting exector "$#"
' "" "${the_args[#]}"

bash set environment variable before command in script

I compile my project with:
debug=yes make -j4 or debug=no make -j4
The debug variable changes some compiler flags in the Makefile
Instead of typing this repeatedly in the shell, I wrote this script (lets call it daemon):
#!/bin/bash
inotifywait -q -m -e close_write `ls *.c* *.h*` |
while read; do
make -j4
done
so I just do ./daemon which automatically builds whenever a file is written to.
However, I would like to be able to pass the debug=no make -j4 to the ./daemon script like this:
./daemon debug=no make -j4
So I modified the script:
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Usage `basename $0` [COMMAND]"
exit 1;
fi
inotifywait -q -m -e close_write `ls *.c* *.h*` |
while read; do
"$#"
done
This works with ./daemon make -j4 but when I say daemon debug=no make -j4 I get the following error:
./daemon: line 9: debug=no: command not found
How can I make it so debug=no is recognized as a variable and not a command in the daemon script?
Thanks
The expansion of "$#" is parsed after any pre-command assignments are recognized. All you need to do is ensure that debug=... is in the environment of the command that runs make, which is your daemon script.
debug=no ./daemon make -j4
Variable expansions will only ever become arguments (including the zeroth argument: the command name).
They will never become:
Redirections, so you can't var='> file'; cmd $var
Shell keywords or operators, so you can't var='&'; mydaemon $var
Assignments, including prefix assignments, so you can't var='debug=yes'; $var make as you discovered
Command expansions, loops, process substitutions, &&/||, escape sequences, or anything else.
If you want to do this though, you're in luck: there's a standard POSIX tool that will turn leading key=value pairs into environment variables and run the program you want.
It's called env. Here's an example:
run() {
env "$#"
}
run debug=yes make -j 4
Though TBH I'd use chepner's solution
You always need to put the (env) variable settings at the beginning of the command, i.e. before "daemon".

Execution on ./ not sh

this is part of my current code
#! /bin/bash
#Take no arguments in
#checks to see if home/deleted is in existence
#creates the directory or file if it is missing
**(line 15)** function checkbin(){
if [ ! -c "~/deleted" ]; then
mkdir -p ~/deleted
fi
if [ ! -f "~/.restore.info" ]; then
touch ~/deleted/.restore.info
fi
}
I can call this code properly using ./remove [ARGS]
however when I call using sh remove [ARGS]
I receive the following error remove: 15: remove: Syntax error: "(" unexpected
ls -l on the file -rwxr-x--x
Does unix support execution on both sh and ./ ?
when executing with ./ /bin/bash is used (as define in shebang) whereas sh may be another interpreter or a link to bash which can have a different behaviour depending on how it is called sh.
bash is derived from sh but has some specific syntax:
for example in sh Function Definition Command is
fname() compound-command[io-redirect ...]
without function keyword.
For more details
Bash
Posix shell
If you want your script to run in sh as well as in bash, you need to be writing to the POSIX shell standard.
In this case, that means not using the Bash function keyword:
checkbin(){
test -c "~/deleted" || mkdir -p ~/deleted
test -f "~/.restore.info" || touch ~/deleted/.restore.info
}
If you're writing portable shell, then it's a good idea to use #!/bin/sh as your shebang.
(BTW, I assume you're aware that "~/deleted" and ~/deleted are in no way alike?)

How to add "&&" in a shell command line in buildbot?

I need to use "&&" to execute multiple commands in one step. So I create factory as below:
f1 = factory.BuildFactory()
f1.addStep(shell.ShellCommand, command=["sh", "-c", "pwd", "&&", "cd", "/home/xxx/yyy", "&&", "pwd"])
But during execution it's found that buildbot processes it as below, which makes it impossible to execute
sh -c pwd '&&' cd /home/xxx/yyy '&&' pwd
What I expected is
sh -c pwd && cd /home/xxx/yyy && pwd
Could anyone help me out of this please? Thanks.
Since you're using /bin/sh anyway just call it with a single string:
f1.addStep(shell.ShellCommand, command="pwd && cd /home/xxx/yyy && pwd")
As documentation says:
The ShellCommand arguments are:
command
a list of strings (preferred) or single string (discouraged) which specifies the command to be run. A list of strings is preferred
because it can be used directly as an argv array. Using a single
string (with embedded spaces) requires the buildslave to pass the
string to /bin/sh for interpretation, which raises all sorts of
difficult questions about how to escape or interpret shell
metacharacters.
It's not recommended but you'd still need the shell anyway as && is only interpreted within the shell. Calling sh would just be redundant.

path of running bash script found when re-runs as sudo

The following code checks if you have root authority, then runs the script again with it :
CMDLN_ARGS="$#" # Command line arguments for this script (if any)
export CMDLN_ARGS
func_check_for_sudo() {
if [ ! $( id -u ) -eq 0 ]; then
echo "You may be asked for your login password for [`whoami`]." ;sleep 1
LAUNCH="`dirname \"${0}\"`"
exec sudo -S su -c ${LAUNCH}/$(basename ${0}) ${CMDLN_ARGS}
exit ${?}
fi
}
Where things are going wrong is when I place this script in a "$HOME/bin" folder or something so I can just launch it without the path. It gives me the error "No such file or directory". I need the script to get that information and correctly pass it to exec.
My question is this: how do I get the /path/to/script_name from within a script correctly when it is called without the path? To recap, I'm calling MY_SCRIPT insead /path/to/MY_SCRIPT which breaks my script because it has to check for root authority and run again if you don't have it.
Basically the line of code in question is this where ${0} is the script name (with path if you called it with one):
exec sudo -S su -c ${0} ${CMDLN_ARGS}
There are a couple of problems here:
Finding the path to the script. There are a couple of easy ways to do this: use "$BASH_SOURCE" instead of $0; or simply take advantage of the fact that (at least by default), sudo preserves $PATH, so sudo "$0" ... will resolve the script fine.
The second is that the script doesn't preserve its arguments properly. Spaces within arguments will be mistaken for breaks between arguments, and wildcards will be erroneously expanded. This is because CMDLN_ARGS="$#" mushes all the arguments together separated by spaces, and then ${CMDLN_ARGS} re-splits on spaces (maybe not the same way) and also expands wildcards.
Here's my take at correcting the problems. Note that putting the handler in a function just adds a layer of unnecessary complication, so I just put it inline. I also used sudo's -p option to clean up the prompting slightly.
if [ $( id -u ) -ne 0 ]; then
exec sudo -p "Login password for %p: " "$0" "$#"
exit $?
fi

Resources