zsh-workers
 help / color / mirror / code / Atom feed
* "jobs" command within substitution
@ 2007-03-08 19:27 Micah Cowan
       [not found] ` <17393e3e0703081155o7a240628t49fae220ac9f49ae@mail.gmail.com>
  0 siblings, 1 reply; 3+ messages in thread
From: Micah Cowan @ 2007-03-08 19:27 UTC (permalink / raw)
  To: zsh-workers

Hello, all. This is my first post to this group.

I scoured the manpages and list archives, but could not find the answer 
I seek.

My question, in a nutshell, is: How can I effectively use the "jobs" 
builtin command in producing the interactive prompt?

-- THE PROBLEM --

The following line was the obvious choice (provided appropriate options 
are set), and indeed works fine in several other shells:

: PS1='$(jobs | pjobs_gen_prompt)'
: # pjobs_gen_prompt is a custom shell fn

However, I've discovered that, the output of

: echo $(jobs)

is empty (apart from the spurious newline), even when executing "jobs" 
in the current shell would produce output. In fact,

: ( jobs )

still produces the expected, same results as execution directly within 
the current shell.

It might be argued, due to the language of POSIX ("The jobs utility 
shall display the status of jobs that were started in the current shell 
environment"), that it is not required to do so for /copies/ of the 
current shell environment: however, the case between $(jobs) and (jobs) 
should at least be consistent, since their behavior in POSIX is defined 
the same. I don't think the language in the standard is precise enough 
to warrant me claiming that this actually /breaks/ POSIX, but it seems 
against the likely /intentions/ at any rate, at least to me.

My personal opinion is that this is a bug, but I'd be happy to hear 
explanations if this is intended behavior.

-- WORKAROUNDS? --

Regardless of whether this is a bug or not, I still need a way to do 
what I want. You can get a very clear picture of what I'm trying to 
accomplish by downloading my script (it lacks spit and polish that I 
plan to add soon, so it's not the "official" version yet; but it's 
functional):

http://micah.cowan.name/svn/promptjobs/trunk/prompt-jobs.sh

^^^ source this, DON'T execute it (it won't work).

and sourcing it (". ./prompt-jobs.sh"). After this, run a couple of jobs 
in the foreground (say, "man man" or "ls | less") and then suspend them 
via Ctrl-Z. In bash, this will produce a prompt such as:

   micah(1:man 2:ls)$

The prompt will be colorized in color-supporting terminals.

I wish to get the same effect in zsh. You can follow the same steps in 
zsh, and will get the colorized version of your prompt, but you won't 
get the joblists.

In the case of bash, I'm actually not using command-substitution on the 
value of PS1, but I'm using bash's PROMPT_COMMAND to /set/ PS1's value 
via a command-substitution. This is because, when bash is interpolating 
the value from PS1, it treats the escape-sequence protectors \[ and \] 
(equivalent to zsh's %{, %}) before it does the command-substitution, 
which would cause pjobs_gen_prompt's \[, \] to be printed literally. Zsh 
treats %{ and %} after command-substitution, so that's not a problem.

Is there any /other/ way to do this that will work, today, for zsh? 
Perhaps an equivalent to bash's PROMPT_COMMAND that I haven't found in 
the documentation, or some option I missed that will allow jobs within 
command substitution to produce the output I'm expecting?

Thanks /very/ much for taking the time to read this length explanation. 
And, please let me know what you think of my little script :)

-- 
Micah J. Cowan
Programmer, musician, typesetting enthusiast, gamer...
http://micah.cowan.name/


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

* Re: "jobs" command within substitution
       [not found]   ` <45F070A9.5060502@cowan.name>
@ 2007-03-08 21:19     ` Matt Wozniski
  2007-03-08 22:00       ` Micah Cowan
  0 siblings, 1 reply; 3+ messages in thread
From: Matt Wozniski @ 2007-03-08 21:19 UTC (permalink / raw)
  To: zsh-workers, zsh-users

On 3/8/07, Micah Cowan wrote:
> Matt Wozniski wrote:
> > On 3/8/07, Micah Cowan wrote:
> >> Hello, all. This is my first post to this group.
> >>
> >> I scoured the manpages and list archives, but could not find the answer
> >> I seek.
> >>
> >> My question, in a nutshell, is: How can I effectively use the "jobs"
> >> builtin command in producing the interactive prompt?
> >
> > Rather than trying to parse the output of the 'jobs' command, you
> > might find yourself better suited by manipulating the variables
> > $jobdirs, $jobstates, and $jobtexts.  You correctly identified the
> > problem that you're hitting - $(jobs) is running in a subshell that
> > doesn't have any jobs in its job table.  I agree, however, that (jobs)
> > should also be blank, since it's also running in a subshell.  If,
> > however, you're dead-set on parsing the output of 'jobs', you could
> > use a syntax like 'jobs > >(read jobtext; echo $jobtext)', which is a
> > clever way to run jobs in the current shell and do the parsing in a
> > subshell via process substitution.
>
> Myself, I'd prefer to see both subshells produce the same output as the
> "current shell", as bash, pdksh and ksh do. However, dash goes the other
> way and makes ( jobs ) emit nothing (for all its claims of being a POSIX
> shell, though, dash is fairly broken in some respects, such as broken
> arithmetic expansion and lack of a line-editor [which POSIX requires]).
> I'd be interested in seeing what the OpenGroup committee has to say
> about it, since the standard is far from clear on the subject.
>
> I was not familiar with the variables you mention above. However, I'm
> not sure they solve the problem, as yet again, invoking them within a
> command-substitution will produce no information. The same problem would
> be true of using the other syntax you describe: I still have no
> available means to run the commands every time the prompt is issued,
> apart from within command substitution, which will kill the job-state
> info. Is there any way to get what I want executing in the "current" shell?
>
> Also, had you meant to post this reply to the list? It appears to have
> been sent to me only.

Yes, of course, I had meant to send it to the list.  My mistake.
*sheepish grin*.  So, you're right - the special variable $jobtexts
wouldn't match up in the subshell, but you could do something like
this:

$ function precmd {
export jt=""
for i in ${(kv)jobtexts}; jt="$jt:${i%% *}"
jt=${jt#:}
}

$ echo $jt
1:find:2:sleep

$ echo $(echo $jt)
1:find:2:sleep

And then you have a not-special, not-array version of jobtexts
exported into the environment of subshells that you can manipulate
however you want - and it even lets you remove the ugly dependence on
awk.  ;-)

(precmd is a function that gets called every time the editor is about
to display a prompt, FYI).

Like I said, though - even though this should take no effort
whatsoever to do with zsh - no more than 5 lines or so - it would be
difficult to maintain compatibility with bash.

The best way I can think of to do what you want, off the top of my
head, is the following:

function precmd {
  psvar[1]=""
  # For each key and each value in jobtexts
  for i in ${(kv)jobtexts}; do
    # Come up with a separator between psvar[1] and this text
    if [[ $sep == " " ]]; then
      sep=":"
    else
      sep=" "
    fi
    # Then tack it on to the end of psvar[1] - Removing from
    # the first space to the end of the element, if it has a space
    psvar[1]="${psvar[1]}$sep${i%% *}"
  done
  # Remove leading space we accidentally inserted
  psvar[1]=${psvar[1]# }
}

and

# Tell the prompt to reference psvar[1] (%?v == psvar[?])
PS1='micah(%1v)'

You don't even need to use prompt_subst.

Frankly, I'd just make your script check for ZSH right off the bat and
do it the simple way, leaving the complicated stuff for shells that
don't give you an elegant solution.

~Matt


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

* Re: "jobs" command within substitution
  2007-03-08 21:19     ` Matt Wozniski
@ 2007-03-08 22:00       ` Micah Cowan
  0 siblings, 0 replies; 3+ messages in thread
From: Micah Cowan @ 2007-03-08 22:00 UTC (permalink / raw)
  To: Matt Wozniski; +Cc: zsh-workers, zsh-users

Matt Wozniski wrote:
> On 3/8/07, Micah Cowan wrote:
>> Matt Wozniski wrote:
>> > On 3/8/07, Micah Cowan wrote:
>> >> Hello, all. This is my first post to this group.
>> >>
>> >> I scoured the manpages and list archives, but could not find the 
>> answer
>> >> I seek.
>> >>
>> >> My question, in a nutshell, is: How can I effectively use the "jobs"
>> >> builtin command in producing the interactive prompt?
>> >
>> > Rather than trying to parse the output of the 'jobs' command, you
>> > might find yourself better suited by manipulating the variables
>> > $jobdirs, $jobstates, and $jobtexts.  You correctly identified the
>> > problem that you're hitting - $(jobs) is running in a subshell that
>> > doesn't have any jobs in its job table.  I agree, however, that (jobs)
>> > should also be blank, since it's also running in a subshell.  If,
>> > however, you're dead-set on parsing the output of 'jobs', you could
>> > use a syntax like 'jobs > >(read jobtext; echo $jobtext)', which is a
>> > clever way to run jobs in the current shell and do the parsing in a
>> > subshell via process substitution.
>>
>> Myself, I'd prefer to see both subshells produce the same output as the
>> "current shell", as bash, pdksh and ksh do. However, dash goes the other
>> way and makes ( jobs ) emit nothing (for all its claims of being a POSIX
>> shell, though, dash is fairly broken in some respects, such as broken
>> arithmetic expansion and lack of a line-editor [which POSIX requires]).
>> I'd be interested in seeing what the OpenGroup committee has to say
>> about it, since the standard is far from clear on the subject.
>>
>> I was not familiar with the variables you mention above. However, I'm
>> not sure they solve the problem, as yet again, invoking them within a
>> command-substitution will produce no information. The same problem would
>> be true of using the other syntax you describe: I still have no
>> available means to run the commands every time the prompt is issued,
>> apart from within command substitution, which will kill the job-state
>> info. Is there any way to get what I want executing in the "current" 
>> shell?
>>
>> Also, had you meant to post this reply to the list? It appears to have
>> been sent to me only.
> 
> Yes, of course, I had meant to send it to the list.  My mistake.
> *sheepish grin*.  So, you're right - the special variable $jobtexts
> wouldn't match up in the subshell, but you could do something like
> this:
> 
> $ function precmd {
> export jt=""
> for i in ${(kv)jobtexts}; jt="$jt:${i%% *}"
> jt=${jt#:}
> }
> 
> $ echo $jt
> 1:find:2:sleep
> 
> $ echo $(echo $jt)
> 1:find:2:sleep
> 
> And then you have a not-special, not-array version of jobtexts
> exported into the environment of subshells that you can manipulate
> however you want - and it even lets you remove the ugly dependence on
> awk.  ;-)
> 
> (precmd is a function that gets called every time the editor is about
> to display a prompt, FYI).

precmd looks to be /exactly/ what I was looking for, then. I'll I was so 
focused on the bashism, that I was searching and re-searching the 
special parameters, rather than special functions. Thanks!

Using jobtexts, etc is probably preferable to using the "jobs" output 
anyway, since zsh's appears to have a non-standard format (splitting a 
single job across lines), which will make my life more difficult. It's 
also probably more precise.

I probably won't lose the awk dependency, though, since the awk script 
also does some clean-up on the command names, and I'll probably add 
support for displaying the /arguments/ to certain commands (such as 
vim), or an alert color for some commands (sudo).

> Like I said, though - even though this should take no effort
> whatsoever to do with zsh - no more than 5 lines or so - it would be
> difficult to maintain compatibility with bash.

Eh, I've managed pretty well so far. Where I can't rely on common 
syntax, I can test for the shell (as you can see by my spamming of the 
shell namespace with vars like PJOBS_ZSH). I'm aiming for compliance 
with as many shells will provide me with a hook to the prompt, and are 
reasonably POSIX-compliant.

> The best way I can think of to do what you want, off the top of my
> head, is the following:

<solution snipped>

That'll make a great reference. I'll need to complicate it a bit what 
with my use of terminal escape sequences (tput output) and whatnot, but 
that makes a great start.

> Frankly, I'd just make your script check for ZSH right off the bat and
> do it the simple way, leaving the complicated stuff for shells that
> don't give you an elegant solution.

Yeah, I'm already doing that (it's just that the zsh-specific solution 
doesn't work). I already have to cater specifically to bash 
(PROMPT_COMMAND), and even the set of shells that will let me hook in 
via prompt substitution each have their unique ways of telling the 
line-editor to ignore the invisible terminal escape sequences I use for 
coloring.

Thanks very much for the help. For some reason, I simply couldn't find 
precmd or the job* vars on my own.

-- 
Micah J. Cowan
Programmer, musician, typesetting enthusiast, gamer...
http://micah.cowan.name/


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

end of thread, other threads:[~2007-03-08 21:52 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-08 19:27 "jobs" command within substitution Micah Cowan
     [not found] ` <17393e3e0703081155o7a240628t49fae220ac9f49ae@mail.gmail.com>
     [not found]   ` <45F070A9.5060502@cowan.name>
2007-03-08 21:19     ` Matt Wozniski
2007-03-08 22:00       ` Micah Cowan

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).