LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   run ps|grep command by script/command line ... ERROR: Unsupported option (BSD syntax) (https://www.linuxquestions.org/questions/programming-9/run-ps%7Cgrep-command-by-script-command-line-error-unsupported-option-bsd-syntax-945847/)

masuch 05-19-2012 03:53 AM

run ps|grep command by script/command line ... ERROR: Unsupported option (BSD syntax)
 
Hi,

I run ps ax|grep something without any problem.
when I put it in script I have got ERROR:


ERROR: Unsupported option (BSD syntax)
********* simple selection ********* ********* selection by list *********
-A all processes -C by command name
-N negate selection -G by real group ID (supports names)
-a all w/ tty except session leaders -U by real user ID (supports names)
-d all except session leaders -g by session OR by effective group name
-e all processes -p by process ID
T all processes on this terminal -s processes in the sessions given
a all w/ tty, including other users -t by tty
g OBSOLETE -- DO NOT USE -u by effective user ID (supports names)
r only running processes U processes for specified users
x processes w/o controlling ttys t by tty
*********** output format ********** *********** long options ***********
-o,o user-defined -f full --Group --User --pid --cols --ppid
-j,j job control s signal --group --user --sid --rows --info
-O,O preloaded -o v virtual memory --cumulative --format --deselect
-l,l long u user-oriented --sort --tty --forest --version
-F extra full X registers --heading --no-heading --context
********* misc options *********
-V,V show version L list format codes f ASCII art forest
-m,m,-L,-T,H threads S children in sum -y change -l format
-M,Z security data c true command name -c scheduling class


....
If I remove grep command - it works.
If I put ps ax|grep --ignore-case something into script ...
this error appeared again - which is confusing because
grep should use "GNU long options" but NOT "BSD options".

run it in command line in terminal - there is no problem at all. Only in script problem appeared.

where is the problem ?
is it some bug of grep command ?

thank you,
kind regards,
Martin

Nominal Animal 05-19-2012 02:27 PM

Quote:

Originally Posted by masuch (Post 4682460)
when I put ps ax|grep something in script I have got ERROR

Because you did not say what kind of script (shell script? Perl? Python) or even what command you used to run ps ax|grep something in your script, it is very difficult to answer anything meaningful. We need the details!

However..

Pipe | is a shell construct. It looks like your command is not run using a shell, but directly, so that instead of ps ax being run with its output piped to grep something (which is all set up by the shell), you only run ps with parameters ax|grep and something.

A simple way to fix that is to explicitly use a shell to run the command. For example,
Code:

sh -c "ps ax | grep something "

masuch 05-19-2012 05:58 PM

OK, thank you for answer.

This problem did not appear long time before - just after I did some changes in one script of complex of shell /bin/bash scripts.
! BUT I did not change "ps ax|grep -i something" at all in that script !
Weird is that after reboot computer - this error disappeared and everything got into normal and works properly. 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 did not have to use "sh -c ..." construct")

You mentioned:
"
...
instead of ps ax being run with its output piped to grep something (which is all set up by the shell), you only run ps with parameters ax|grep and something.
...
"

How can I determinate the way how the shell is going to interpreted command "(parameters or piped in this particular case)" ?
... any help/hint/URLs/doc much appreciate.

thank you,
kind regards,
M.


P.S.:
---- I am using this almost on the beginning of that specific bash script:
CMDLN_ARGS="$@"
export CMDLN_ARGS
# Run this script as root if not already.
chk_root () {
if [ ! $( id -u ) -eq 0 ]; then
my_fake_constructor_function
echo "Please enter root's password."
exec su -c "("${0}" ${CMDLN_ARGS} 2>&1) | tee ${_log_file}"
RETVAL=${?}
echo "------ ERROR: something really bad happened !"
echo "------ return code=${RETVAL}"
exit ${RETVAL}
else
my_fake_constructor_function
fi
}
chk_root

hope it does not cause that mentioned problem (when I added this small part) but 'ps ax|grep --ignore-case "something"' works now.
???

Nominal Animal 05-20-2012 02:10 PM

Quote:

Originally Posted by masuch (Post 4682881)
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 (Post 4682881)
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,

masuch 05-23-2012 04:13 AM

Hi,

Thanks a lot for this induction to bash scripting - I really appeciate it.
I am slowly implementing your suggestions to many scripts - using combinations of your suggestions.
(I have noticed as well that just in only one case - I had to put command 'ps aux|grep -i "/usr/bin/psensor-server &"' at the begging of the line - if I had some spaces at the begining of the line - it went wrong - do not why.


--- in development is following example - no ammended version yet. Beggining of the shell scripts:
#!/bin/bash
my_fake_constructor_function() {
# get directory of this script:
_SCRIPT_PATH="${BASH_SOURCE[0]}";
if([ -h "${_SCRIPT_PATH}" ]) then
while([ -h "${_SCRIPT_PATH}" ]) do _SCRIPT_PATH=`readlink "${_SCRIPT_PATH}"`; done
fi
_SCRIPT_PATH=`readlink -f $(dirname "${_SCRIPT_PATH}")`

# get timestamp now:
_datetimenow=`date +%Y%m%d--%H-%M-%S`

# log file
#mkdir -p "${_SCRIPT_PATH}/_logs"
_log_file="${_SCRIPT_PATH}/`hostname`--${0##*/}--${_datetimenow}.log"
}

CMDLN_ARGS="$@"
export CMDLN_ARGS
chk_root () {
if [ ! $( id -u ) -eq 0 ]; then
my_fake_constructor_function
echo "Please enter root's password."
exec su -c "("${0}" ${CMDLN_ARGS} 2>&1) | tee ${_log_file}"
RETVAL=${?}
echo "------ ERROR: something really bad happened !"
echo "------ return code=${RETVAL}"
exit ${RETVAL}
else
my_fake_constructor_function
fi
}
chk_root
sudo echo ""
...



(--- basicaly it runs script as root with output as on the screen as to the log file (which is my intention).)



--- one part of the script - one function (ps command)
(it is testing if application runs (by pidof and ps) - if not - it is going to start that application
(but I still have some problems to solve - like DBUS_SESSION_BUS_ADDRESS - as you can see :-)



startrestartaplls() {
y=`expr $# / 2`
for (( x=0; x<y; x++ )); do
LIST1[x]="$1"
LIST2[x]="$2"
shift
shift
done
for (( A=0; A<$y; A++ )); do # ${#LIST1[*]}/2
echo ""
echo "---------------------------------------------------------------------- '${LIST1[$A]}' '${LIST2[$A]}'"
_process="${LIST2[$A]}"
_pidof=`pidof ${_process}`
RETVAL=$?
if [ "${RETVAL}" == "0" ]; then # 0 At least one program was found with the requested name.
if [ -z "${_pidof}" ]; then
echo " --- process is NOT running: ${_process}"
ps aux|grep -i ${_process}
else
echo " --- process is running: ${_process} with PID=${_pidof}"
fi
elif [ "${RETVAL}" == "1" ]; then # 1 No program was found with the requested name.
echo " --- process does NOT EXIST: ${_process}"
ps aux|grep -i ${_process}
#su --login root --shell /bin/bash --command "${LIST1[$A]}"
echo "DBUS_SESSION_BUS_ADDRESS="$DBUS_SESSION_BUS_ADDRESS
su --login root --preserve-environment --command "${LIST1[$A]}"
RETVAL=$?
echo "retval=$RETVAL"
case "$RETVAL" in
"0")
echo "success (--help only)" >/dev/null
;;
"1")
echo "ERROR: System or authentication failure."
;;
"126")
echo "ERROR: The requested command was not found."
;;
"127")
echo "ERROR: The requested command could not be executed."
;;
*)
echo "ERROR: Some weird error happened."
;;
esac
else
echo " --- pidof returned ERROR=${RETVAL}"
fi
done
}

--- one of the input is like following:
_appl_2x_array=("/usr/bin/pglcmd restart" "pgld" "/opt/xplico/script/sqlite_demo.sh &" "xplico" "/usr/local/sbin/dnscrypt-proxy --daemonize &" "dnscrypt-proxy" "/usr/bin/psensor-server &" "psensor-server" "vidalia &" "vidalia")

--- At this moment it is under development - something works something not
(example: 'vidalia &' does not work - if executed from the script but works from terminal command line) :-)

--- I cannot put here even one whole script - because it is long and under re/development.
--- In some cases I do not have to use su (can use sudo) - in some scripts I have to :-)


thank you again - your suggestions are going to be implemented in many scripts :-)


All times are GMT -5. The time now is 09:02 AM.