zsh-users
 help / color / mirror / code / Atom feed
* Honoring a command
@ 2004-04-01 17:03 DervishD
  2004-04-03 21:43 ` Bart Schaefer
  0 siblings, 1 reply; 5+ messages in thread
From: DervishD @ 2004-04-01 17:03 UTC (permalink / raw)
  To: Zsh Users

    Hi all :)

    A couple of weeks ago one of the list members asked about
justifying some script output, and I decided to er.. inspire from
(that is, steal) that idea and I want to modify some scripts I have
from something like:

    Printing file whatever.ps... done.

    to something like:

Printing file whatever.ps                                    [ok]

    justified to the screen width.

    This is not a problem since thanks to this list I know how to do
the justification, but the question is that I want to modify code
like this:

    print -n "Doing whatever command..." >&2
    whatever.command 2> /dev/null || { print " error! Message" >&2; return 1; }
    print " done." >&2


    to this:

    verbosely_do "Doing whatever command" whatever.command \
        || { print "Message" >&2; return 1;}

    The function 'verbosely_do' is pretty easy:

    function verbosely_do() {
    
        emulate -L zsh

        print -n ${(r:(($WIDTH)):)1} >&2
        $* || {
             print "[!]" >&2
             return 1
        }
        print "[*]" >&2
        return 0
    }

    $WIDTH is defined elsewhere or can be even a fixed number, and
the symbols may have colors, etc... The important point is that the
function prettyprints some messages and run the commands. Please,
pretend that you don't notice that bare '$*' that may expand to an
empty string ;)) That will corrected later.

    The problem I see is: what will happen if the command has
redirections, metacharacters, quotes, variable references, etc.? Will
it be run by 'verbosely_do' correctly, that is, exactly in the same
way as it was run in the code without 'verbosely_do', or should I
preprocess it in some way? As an example:

    verbosely_do "Command" chown -R `id -un`:`id -gn` "$SOURCES"

    Although it may be any command that will run correctly given
itself alone in any script.

    Thanks a lot and please excuse me for such weird question O:)

    Raúl Núñez de Arenas Coronado

-- 
Linux Registered User 88736
http://www.pleyades.net & http://raul.pleyades.net/


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Honoring a command
  2004-04-01 17:03 Honoring a command DervishD
@ 2004-04-03 21:43 ` Bart Schaefer
  2004-04-04 11:16   ` DervishD
  0 siblings, 1 reply; 5+ messages in thread
From: Bart Schaefer @ 2004-04-03 21:43 UTC (permalink / raw)
  To: Zsh Users

On Apr 1,  7:03pm, DervishD wrote:
} 
} [...] the question is that I want to modify code like this:
} 
}     print -n "Doing whatever command..." >&2
}     whatever.command 2> /dev/null || { print " error! Message" >&2; return 1; }
}     print " done." >&2
} 
} 
}     to this:
} 
}     verbosely_do "Doing whatever command" whatever.command \
}         || { print "Message" >&2; return 1;}

If you have access to the "initscripts" package, you might take a look
at /etc/rc.d/init.d/functions -- particularly the "action" function
that is defined in that file.  It does pretty much exactly what your
"verbosely_do" is meant to do (with the addition of wrapping the call
in something called "initlog" which makes syslog entries for the
command, but you can take that out).

Something to note about that "action" function is that it folds the
failure message into the wrapper function rather than following the
wrapper call with an or-command.

}     The problem I see is: what will happen if the command has
} redirections, metacharacters, quotes, variable references, etc.?

Unless you quote them, they'll all be processed BEFORE verbosely_do
is called, which may or may not do what you want.

If you do quote them, then you'll probably need to use
	eval "$*"
inside the verbosely_do function, which of course has it's own set of
problems.  As far as I can tell, the "action" function I mentioned
assumes that there are no redirections and that it's OK for metachars
and so on to have been expanded before the call.

I'd also note that both "action" and "verbosely_do" are assuming that
whatever.command does not produce any standard output of its own, or
else that it's been redirected away somewhere (which is part of what
"initlog" does, I think).

Given that the command itself produces no output, you might consider
something like this:

    verbosely_watch() {
    	emulate -L zsh
	local output
	print -u2 -n ${(r:WIDTH:)1}
	if read output
	then
	    print -u2 $output
	else
	    print -u2 '[ok]'
	fi
    }

    TRAPZERR() { print '[fail]' }
    { whatever.command } 2>/dev/null | verbosely_watch "Doing whatever"

That allows any syntax you like within the { }.


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Honoring a command
  2004-04-03 21:43 ` Bart Schaefer
@ 2004-04-04 11:16   ` DervishD
  2004-04-05  1:09     ` Bart Schaefer
  0 siblings, 1 reply; 5+ messages in thread
From: DervishD @ 2004-04-04 11:16 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

    Hi Bart :)

 * Bart Schaefer <schaefer@brasslantern.com> dixit:
> }     verbosely_do "Doing whatever command" whatever.command \
> }         || { print "Message" >&2; return 1;}
> If you have access to the "initscripts" package, you might take a look
> at /etc/rc.d/init.d/functions -- particularly the "action" function
> that is defined in that file.  It does pretty much exactly what your
> "verbosely_do" is meant to do (with the addition of wrapping the call
> in something called "initlog" which makes syslog entries for the
> command, but you can take that out).

    I don't have that package (well, I've took a look at the
initscripts package for Debian, but I think you were talking about
the RPM one)

> I'd also note that both "action" and "verbosely_do" are assuming that
> whatever.command does not produce any standard output of its own, or
> else that it's been redirected away somewhere (which is part of what
> "initlog" does, I think).

    Yes, in all my shell functions the command does not spit anything
through stdout (or has it redirected as well). That's the reason of
the '2>' in my examples: I really don't care about stdout because it
is handled, but stderr is not.
 
> Given that the command itself produces no output, you might consider
> something like this:

    Bart, you're a genius :)) This solution is simpler, more
powerful and flexible since it doesn't pass the command as a
parameter, shorter, easier to understand, does not mess a lot with
AND lists or OR lists, etc... The number of beers I owe you is
growing dangerously ;)
 
>     verbosely_watch() {
>         emulate -L zsh
>         local output
>         print -u2 -n ${(r:WIDTH:)1}
>         if read output
>         then
>             print -u2 $output
>         else
>             print -u2 '[ok]'
>         fi
>     }
> 
>     TRAPZERR() { print '[fail]' }
>     { whatever.command } 2>/dev/null | verbosely_watch "Doing whatever"

    I'm going to change it a bit, for not using TRAPZERR: I use it
for other purpose in my scripts, although that doesn't seem to work
:( (see below)

    verbosely_watch() {
        emulate -L zsh
        local output
        print -u2 -n ${(r:WIDTH:)1}
        if read output
        then
            print -u2 '[fail]'
            print $output
        else
            print -u2 '[ok]'
        fi
    }

    And after that, I'm going to use it as:

    { whatever.command 2> /dev/null || print "Error message"} \
        | verbosely_watch "Doing whatever"

    I have the following alias, called 'scriptinit', which is called
in all my scripts:

    alias scriptinit=$'emulate -L zsh ; trap \'return $LINENO\' ZERR'

    That way, anytime a script fails, its return code tells me the
line the failure took place, and I can have silent scripts for some
tasks, which still give information about what caused the error (if
any). The problem is that it doesn't work with the above usage of
verbosely_watch, because the trap is never run due to the OR list. I
must replace:

    { whatever.command 2> /dev/null || print "Error message"}

    by:

    { whatever.command 2> /dev/null || print "Error message";false}

    for my generic trap to work :(, but then 'verbosely_watch' has no
purpose at all! I think I'd better use 'scriptinit' only for muted
scripts and verbosely_watch for the rest, using TRAPZERR for the
error message. Is that a good idea?

    Thanks a lot Bart, as always.

    Raúl Núñez de Arenas Coronado

-- 
Linux Registered User 88736
http://www.pleyades.net & http://raul.pleyades.net/


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Honoring a command
  2004-04-04 11:16   ` DervishD
@ 2004-04-05  1:09     ` Bart Schaefer
  2004-04-05  9:59       ` DervishD
  0 siblings, 1 reply; 5+ messages in thread
From: Bart Schaefer @ 2004-04-05  1:09 UTC (permalink / raw)
  To: Zsh Users

On Apr 4,  1:16pm, DervishD wrote:
}
} The number of beers I owe you is growing dangerously ;)

Especially considering that I rarely drink beer.  Send me a six-pack of
some unusual Spanish soft drink, or something. ;-}
  
}     alias scriptinit=$'emulate -L zsh ; trap \'return $LINENO\' ZERR'

An interesting tidbit I just noticed -- if you read the script with the
"source" or "." commands, $LINENO is reset to 1 when the trap runs, so
you don't get as useful a return value.

} The problem is that it doesn't work with the above usage of
} verbosely_watch, because the trap is never run due to the OR list. I
} must replace:
} 
}     { whatever.command 2> /dev/null || print "Error message"}
} 
}     by:
} 
}     { whatever.command 2> /dev/null || print "Error message";false}

Actually that won't work either, because that _always_ executes "false".
I think you meant

  { whatever.command 2> /dev/null || { print "Error message";false } }

} but then 'verbosely_watch' has no purpose at all!

You could write another little function:

    verbosely_fail() {
	local ret=$?
	[[ -p /dev/fd/1 ]] && print "$*"
	return ret
    }

Now this works:

  { whatever.command || verbosely_fail "Error message" } 2>/dev/null |
  	verbosely_watch "Doing whatever"

If you DON'T pipe to verbosely_watch, then verbosely_fail is silent and the
ZERR trap returns the line number as $?.  (Even if you do pipe it, the line
number is stored in $pipestatus[1], which may be useful for other tricks.)

} I think I'd better use 'scriptinit' only for muted
} scripts and verbosely_watch for the rest, using TRAPZERR for the
} error message. Is that a good idea?

That would work as well, but it means the error message is the same for
every command (unless you re-assign it somehow each time).


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Honoring a command
  2004-04-05  1:09     ` Bart Schaefer
@ 2004-04-05  9:59       ` DervishD
  0 siblings, 0 replies; 5+ messages in thread
From: DervishD @ 2004-04-05  9:59 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

    Hi Bart :)

 * Bart Schaefer <schaefer@brasslantern.com> dixit:
> } The number of beers I owe you is growing dangerously ;)
> Especially considering that I rarely drink beer.  Send me a six-pack of
> some unusual Spanish soft drink, or something. ;-}

    I think the only soft drinks we have in Spain are from abroad,
but I'll take a look ;))
   
> }     alias scriptinit=$'emulate -L zsh ; trap \'return $LINENO\' ZERR'
> An interesting tidbit I just noticed -- if you read the script with the
> "source" or "." commands, $LINENO is reset to 1 when the trap runs, so
> you don't get as useful a return value.

    I know, but I never source that scrips (is there any way to make
a script as 'unsourceable' :?). This ZERR thing for returning an
error code is not very solid :( I should work in a better one, but I
had never the need O:)

> }     { whatever.command 2> /dev/null || print "Error message";false}
> Actually that won't work either, because that _always_ executes "false".
> I think you meant
>   { whatever.command 2> /dev/null || { print "Error message";false } }

    Yes, I forgot the braces, sorry O:)
 
> } but then 'verbosely_watch' has no purpose at all!
> You could write another little function:
>     verbosely_fail() {
> 	local ret=$?
> 	[[ -p /dev/fd/1 ]] && print "$*"
> 	return ret
>     }

    That's nice :)
> 
> Now this works:
> 
>   { whatever.command || verbosely_fail "Error message" } 2>/dev/null |
>   	verbosely_watch "Doing whatever"
> 
> If you DON'T pipe to verbosely_watch, then verbosely_fail is silent and the
> ZERR trap returns the line number as $?.  (Even if you do pipe it, the line
> number is stored in $pipestatus[1], which may be useful for other tricks.)
> 
> } I think I'd better use 'scriptinit' only for muted
> } scripts and verbosely_watch for the rest, using TRAPZERR for the
> } error message. Is that a good idea?
> That would work as well, but it means the error message is the same for
> every command (unless you re-assign it somehow each time).

    That's not a problem, I can use a global variable for the message
and reassign it before each command is run, instead of redefining
TRAPZERR (and obviously I must make TRAPZERR output that variable).

    Finally I think that the solution I'm going to adopt is the
function 'verbosely_watch', TRAPZERR for the error message instead
the OR list. I'm afraid that using ZERR to return the line number of
the error is only useful for debugging, to know where a bug happened,
but for production scripts I think is better to have a reduced and
well defined set of error return codes.

    Thanks a lot for your, as always, invaluable help.

    Raúl Núñez de Arenas Coronado

-- 
Linux Registered User 88736
http://www.pleyades.net & http://raul.pleyades.net/


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2004-04-05 10:18 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-04-01 17:03 Honoring a command DervishD
2004-04-03 21:43 ` Bart Schaefer
2004-04-04 11:16   ` DervishD
2004-04-05  1:09     ` Bart Schaefer
2004-04-05  9:59       ` DervishD

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).