Quote:
Originally Posted by masuch
So, technically I could marked this post as solved but I am very interested how to investigate more deeply - what actually happened and why ?
|
I'd need a lot more details -- the exact file, the exact commands, and the exact error messages -- to be confident, but based on the effects and the snippet, I'd say either the
su default shell was temporarily changed (by setting the
SHELL variable), or the shell options related to parameter expansions had changed.
I am quite confident the error was related to expansion: that instead of being parsed as a shell command line, it was parsed as a single command. In other words, the pipe character (
|) was not interpreted by the shell at all. It does not matter at all which commands you had on either side. If the commands did not have any quote characters, then they could not have affected this at all; it would have happened to any command. If you did use quotes, then you might have accidentally quoted the pipe too; perhaps by having unbalanced quotes.
I wouldn't worry about it, though, unless it reoccurs.
The way you have constructed your script is very fragile. In particular,
Code:
CMDLN_ARGS="$@"
exec su -c "(\"${0}\" ${CMDLN_ARGS} 2>&1 )" | tee ${_log_file}"
is not safe; it causes the command line to be re-split if not run as root, with expansions done twice. In other words, it has very different behavior wrt. command line parameters when run as root, than when run as any other user. Dangerous.
In your case, if you could use
sudo instead of
su, you could use
Code:
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
sudo -- "$0" "$@"
exit $?
fi
# You are now root, do the rest of the stuff...
If you have to use
su, then you need to convert the parameters to a single string, which is quite cumbersome. I think this should work, though:
Code:
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
argstring=""
squote="'\"'\"'"
for arg in "$0" "$@"; do
argstring="$argstring'${arg//\'/$squote}' "
done
argstring="${argstring% }"
exec su --shell /bin/bash -c "$argstring"
exit $?
fi
# You are now root, do the rest of the stuff...
The above constructs a string containing each argument in single quotes into
argstring. The only character that should need any escaping is the single quote itself. It cannot be escaped, but you can temporarily switch to double quotes;
'foo'"'"'bar' evaluates to
foo'bar . The first argument is of course the path to this script (
$0), which needs to be escaped just like the others.
EDIT: Bash built-in
printf might actually be a much easier solution for this:
Code:
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
exec su --shell /bin/bash -c "$(printf '%q ' "$0" "$@")"
exit $?
fi
# You are now root, do the rest of the stuff...
To handle the logging in the rest of your command -- I'd personally try hard to avoid logging the
sudo or
su password conversation --, I would suggest continuing the script with
Code:
# You are now root, do the rest of the stuff...
( # If logging, combine stderr to stdout.
[ -n "$_log_file" ] && exec 2>&1
#
# Do the real work in this part!
#
) | (
[ -n "$_log_file" ] && exec tee "${_log_file}"
exec cat
)
This way you can just unset or clear
_log_file to empty string (before this part is executed, of course!), to disable logging. If logging is NOT used, then the script will keep stdout and stderr separate, which is sometimes quite useful.
The logic is as follows:
If we do not have a log file, then the standard error of the work part goes directly to standard error (NOT via the pipe), and standard output to standard output via the pipe and
cat.
If we do have a log file, then standard error is merged to standard output in the subshell, so that its standard error is also redirected to the pipe. At the other end of the pipe,
teewill duplicate everything to both the log file and to standard output.
Although we use a subshell for both the work and the logging, the
execs in the latter replace the subshell with the actual command. Thus, there is only one "extra" subshell, the one doing the work.
Note that you do need to do all work where I added the command. Not only because only that part will be logged, but also because it is a subshell, and any changes it makes to variables or environment will not be visible in the parent shell. Other than that, there should be no quirks to worry about.
Quote:
Originally Posted by masuch
How can I determinate the way how the shell is going to interpreted command
|
The
Shell Expansions section in the
Bash Reference Manual describes it in detail. Also see the
Quoting chapter for details on proper quoting.
Any questions?
I hope you find this useful,