Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

a couple of unixy questions...

In post http://discussions.apple.com/thread.jspa?threadID=728239, Cole Tierney gave an example of a unix test:
if (netstat -rn | grep -q ^default); then echo online; fi
So, my first question: how come the use of parentheses and not brackets, like
if [ netstat -rn | grep -q ^default ]; then echo online; fi
with a reason more fulfilling than "Because it doesn't work with brackets."

My next topic, /bin/sh syntax questions:
Assume an if-then-{elif}-{else}-fi or a while/until/for-do-done construct. Suppose the test condition is a boolean AND or boolean OR of two or more conditions. Would an example of proper syntax be
until A || B;do
or does the conditional " A or B" need to be grouped in parentheses, brackets, braces, or whatever, as a single argument for the until keyword, kinda like this?
until (A || B);do

What if you had to group a more complex expression of boolean items, such as A and (B or (not C))? What would be the proper way to make the construct?

And lastly (woo-hoo!), suppose you've got a bash script startup item that is supposed to wait until the occurrence of an event before continuing? Well, I implemented that inside a until ...; do; sleep ${T}; done construct. Works like a charm, as long as the event eventually happens.

But, suppose the event doesn't happen, and the computer user issues a shutdown or reboot command from the blue apple in the menubar, or from the user login screen? Well, what happens is that the computer hangs in the reboot or shutdown process because since the wantedevent never occurred in the startup item script, the bash script is still infinitely looping in the until {wantedEvent} loop. That's no bueno.

I'd like to do a construct like this:
until ( {wantedEvent} || {shutdown} || {restart} ); do
So, I'm thinking that if I were to check for the existence of a pid for shutdown and/or reboot like this (put these two lines of code inside my until construct, with my sleep ${T})
pid1=`ps -axwww | grep '/\shutdown ' | awk '{ print $1 }'`
pid2=`ps -axwww | grep '/\restart ' | awk '{ print $1 }'`

and build the until statement like this
until ( {wantedEvent} || [ -n "${pid1}" ] || [ -n "${pid2}" ] ); do
then presumably, if either the wanted event occurs, or we sense a shutdown or reboot command from the user, we escape the until loop, the script file can continue to normal completion, and if it were a shutdown or reboot event, we're not left hangin' out to dry having to do a ruthless stop (followed by the ceremonious /sbin/fsck -yf in single-user mode).

If that looks like a viable approach to the experts here, then the next big question du jour is: are shutdown and restart the actual unixy names of these processes that I would want to use in these variable assignment statements ( pid1=..., pid2=...)???

Whew! Lotsa stuff!

TIA

2001 Quicksilver G4 (M8360LL/A), Mac OS X (10.4.8)

Posted on Nov 13, 2006 9:22 PM

Reply
6 replies

Nov 14, 2006 9:14 AM in response to j.v.

So, my first question: how come the use of
parentheses and not brackets, like
if [ netstat -rn | grep -q ^default ]; then
echo online; fi

Because '[' is a synonym for test(1), and test(1) doesn't execute shell expressions. OTOH, '( expression)' means "execute shell expression expression in a sub-shell". "netstat -rn | grep -q ^default" is a shell expression, so it can't be used within '[]'; it must be used within '()'.

Assume an if-then-{elif}-{else}-fi or a
while/until/for-do-done construct. Suppose the
test condition is a boolean AND or boolean OR of two
or more conditions. ...


I confess I do not know the answer, but since it is always OK to replace a boolean expression with the same boolean expression parenthesized, I would go with encompassing the whole multi-term expression within '()'. If all of the terms are test(1) constructs, then you can use test(1)'s "-a" and "-o" to do the ANDs and ORs within one '[]' and skip the encompassing '()'.


Powerbook G4 1GHz Mac OS X (10.3.9)

Nov 18, 2006 8:02 PM in response to Karl Zimmerman

okay ... so I kinda get it ... I think ...

In the "netstat" example, then, since it is executing a shell expression, in this particular instance, if it "greps" and returns a null string, then the if evaluates as false, but if the shell expression "greps" the netstat response and returns a non-null string, then the if evaluates as true???

Enclosing booleans in parentheses ... syntax ... do the parentheses require intervening spaces like the brackets in test ( [) do?

Syntactically, is the following example correct (and I ask because no matter the gazillions of iterations of tweaks in a script I've been working on, the ***** thing keeps crashin' and burnin' and the error message doesn't make sense, claims its a problem on another line, but when I # this guy out, the script runs sorta ok, so I'm trying to get expert advice to conclude that the syntax is or is not the problem)?

Example:
if ((netstat -rn | grep -q ^default) && [ -n "${A}" ] || [ -z "${B}" ]); then

Nov 19, 2006 1:05 AM in response to j.v.

Hi j.v.,

I'd like to do a construct like this:
until ( {wantedEvent} || {shutdown} || {restart} ); do


You will not able to catch the shutdown by this kind of construct, even if you solve syntax problems. But

if it were a shutdown or reboot event, we're not left hangin' out to dry having to do a ruthless stop


you don't have to worry about this. When user select shutdown/reboot, your script will be forced to quit by the operating system.

if (netstat -rn | grep -q ^default); then echo online; fi


Here, the parentheses are redundant; the following can do the same job:

if netstat -rn | grep -q ^default; then echo online; fi

I think the first thing you should do is to read the bash man page, as Cohi writes. In the bash man page, you will find the description of the 'if then fi' command:

if list1; then list2; fi

This means
(1) execute list1
(2) if the exit status of list1 is zero then execute list2

('exit status' may sometime be called as 'retun status', 'return code' or 'exit code')

If you don't understand what the 'list' means, read the bash man page one more time. In

if command1 | command2

the list is 'command1 | command2', and its exit status is that of command2.


if it "greps" and returns a null string, then the if evaluates as false


No. 'grep' is called with an option '-q' so it will return no string even if it finds '^default'. Whether '^default' is found or not is returnd as its exit status (zero=found, non-zero=not found).

PowerMac G4 Mac OS X (10.4.8)

Nov 19, 2006 11:13 AM in response to j.v.

Hi J.V.,
I apologize profusely for the tardiness of this post. I started it at work when you first started this thread. However, we've been in crisis mode for more than a week and I was only able to work on it sporadically. Then I had to reboot that machine and lost the post. This will be shorter than the original. However, I think the viewpoint will justify it.

I think that the most important missing ingredient is that the "if" conditional make its decision based on the exit status of the command that follows it. That is different from other programming languages. The shell "pretends" to be like other languages by providing a special command, '[', and relatives. In other languages this would be part of the syntax of "if" but in the shell, it is actually a command. As Karl points out, you can even see in /bin and read about it with "man test", although most shells implement it and its relatives as builtin commands, to make it easier to handle the close of the bracket. This command performs the various tests with which you are familiar but it returns an exit status like any command and it is this exit status that determines the branching in the "if" clause. However, don't let the similarity between this command and the syntax of "if" expressions in other languages suggest a limitation to you; you can put any command after the "if" because they all return an exit status.

Of course some commands are more useful than others. Most commands only return a failed exit status when they encounter exceptional circumstances. It can always be important to know when a command "hits the wall" but some commands "break the rules" and return a failed exit status under less than exceptional circumstances. These can be particularly useful in tests and an example is "grep", which returns a failed exit status if no match is found. If the search was conducted without difficulty, failing to find a match isn't technically an exceptional circumstance but the ability to branch based on the existence of a matching string is extremely useful in conditional statements. As we discovered, "ping" is another such command.

Thus unlike other languages, shell conditionals do not require any parentheses, brackets or braces. Shells have implemented commands that look like the tests of other languages but these are not part of the syntax of shell conditionals per se. All you really need is "if" followed by a command. Of course this can be a complex command and that's where Jun T.'s discussion of lists comes in. My post is essentially a duplicate of his; I just thought it useful to discuss simple commands first. As Jun points out, it is necessary to understand how the exit status of a complex command is determined. In all cases with which I'm familiar, the exit status of a complex command is determined by the exit status of the last command executed. For instance, the exit status of a pipe is the exit status of the last command in the pipe.

A list of commands connected by boolean operators (!, &&, ||) is theoretically different but execution of the list stops as soon as the exit status of the boolean expression can be determined so the exit status of the list is still determined by the last command executed. Again parentheses aren't required but they can be used in lists to determine order of precedence. One thing that I don't know is whether the use of parentheses in this way causes execution in a subshell; does anyone know about that?

I tested your example and even took it a step further, putting the process test in a function. The script looked like the following:

#!/bin/bash
function pgrep
{
ps -axwwo command | grep -v "[p ]grep" | grep -i "${1}"
}

until pgrep echo || pgrep Chicago || pgrep Raleigh; do
sleep 1
done
echo "Found it"

Note that the exit status of the function is the exit status of the last command executed and that is the pipe. The exit status of the pipe is the exit status of the last command in the pipe, which is why I grepped for the function argument last. The script functioned as it should but of course it rarely caught a process unless it took a long time to run. (a second or more)

I don't understand how a script can block shutdown. I can certainly kill the above script. I could see that launchd might try to restart it but I would expect that to change at shutdown. You said this script was launched as a StartupItem, right? Did you implement the StopService function? If the actual StartupItem invokes a separate script, it should work to put a "killall <scriptname>" in the StopService function. If the StartupItem itself does the monitoring then the StopService function could stop the loop, say by setting a variable whose value is tested in the loop.

In order to make further suggestions about the blocked shutdown, we would have to know the details of your implementation. However, I can't imagine the problem to be insoluble.
--
Gary
~~~~
Kirk to Enterprise -- beam down yeoman Rand and a
six-pack.

a couple of unixy questions...

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.