* implementing a control for completing filenames with a defined list of tokens @ 2013-12-02 14:26 Eric Smith 2013-12-02 15:58 ` Bart Schaefer 0 siblings, 1 reply; 29+ messages in thread From: Eric Smith @ 2013-12-02 14:26 UTC (permalink / raw) To: Zsh Users Wise zsh'lers, I would like to define a completion widget to complete filenames with a defined list of tokens. The widget would look up in a list the tokens and suggest these (ideally in order) to the user. As an example; $ cat tokenfile token1 token2 token3 User has the binding control-k to execute the completion function; mv <somefile> <c-k> Then the widget would supply mv <somefile> token1 token1 token2 token3 How would I best implement this? -- Eric Smith ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: implementing a control for completing filenames with a defined list of tokens 2013-12-02 14:26 implementing a control for completing filenames with a defined list of tokens Eric Smith @ 2013-12-02 15:58 ` Bart Schaefer 2014-03-16 14:13 ` Eric Smith 0 siblings, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2013-12-02 15:58 UTC (permalink / raw) To: Zsh Users On Dec 2, 3:26pm, Eric Smith wrote: } Subject: implementing a control for completing filenames with a defined li } } I would like to define a completion widget to complete filenames with } a defined list of tokens. The widget would look up in a list the } tokens and suggest these (ideally in order) to the user. This one is about as straightforward as it's possible to get. First you need a function that reads tokenfile and passes the lines therein as arguments to the "compadd" builtin. _tokens() { compadd ${(f)"$(<tokenfile)"} } Next you need a completion widget. There's a ready-made function for creating completion widgets, called _generic. zle -C token-completion complete-word _generic bindkey ^K token-completion (^K is normally kill-line so you might want to pick another binding.) Finally tell the completion system that when the token-completer is invoked, it should use the _tokens function to supply the matches: zstyle ':completion:token-completion:*' completer _tokens And you're done. If you eventually want it to complete other things, you can append more functions to the zstyle. Another way to do this is to create a file in your $fpath having a name starting with underscore, and begin that file with a "#compdef -k ..." line. That could be as simple as this: ---- 8< ---- snip ---- 8< ---- #compdef -k complete-word ^K _tokens() { compadd ${(f)"$(<tokenfile)"} } _generic _tokens ---- 8< ---- snip ---- 8< ---- However, that will redefine the _tokens function on each completion. A slightly better formulation would be to have the file redefine the same function as its file name. If the file is named "_token_completion": ---- 8< ---- snip ---- 8< ---- #compdef -k complete-word ^K _tokens() { compadd ${(f)"$(<tokenfile)"} } _token_completion() { _generic _tokens } _token_completion ---- 8< ---- snip ---- 8< ---- It's probably not necessary to go this far when _tokens is so simple, but if you eventually write something more involved it's a good pattern. Note that with the file you don't need the zstyle: passing _tokens as an argument to _generic has the equivalent effect. You could omit _tokens there and instead have the style, just replace "token-completion" in the style pattern with the name of the file. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: implementing a control for completing filenames with a defined list of tokens 2013-12-02 15:58 ` Bart Schaefer @ 2014-03-16 14:13 ` Eric Smith 2014-03-16 19:27 ` Bart Schaefer 0 siblings, 1 reply; 29+ messages in thread From: Eric Smith @ 2014-03-16 14:13 UTC (permalink / raw) To: Zsh Users Thank you Bart. I want to use this completion facility at the start of file (a dir) names and also *in the middle* of the name. So the tags may appear anywhere in the filename. So I could type foobar__^K for example and the function will complete with my tokens which all begin with a double underscore. $ cat tokenfile __job__1403-whatever__ __article__ __finance__ __contract__ __published__ __project-foo__ __job__1402-visit__ How may I adapt your suggestions for this? Thanks a lot. Eric Bart Schaefer wrote on Mon-02-Dec 13 4:58PM > On Dec 2, 3:26pm, Eric Smith wrote: > } Subject: implementing a control for completing filenames with a defined li > } > } I would like to define a completion widget to complete filenames with > } a defined list of tokens. The widget would look up in a list the > } tokens and suggest these (ideally in order) to the user. > > This one is about as straightforward as it's possible to get. > > First you need a function that reads tokenfile and passes the lines > therein as arguments to the "compadd" builtin. > > _tokens() { compadd ${(f)"$(<tokenfile)"} } > > Next you need a completion widget. There's a ready-made function for > creating completion widgets, called _generic. > > zle -C token-completion complete-word _generic > bindkey ^K token-completion > > (^K is normally kill-line so you might want to pick another binding.) > > Finally tell the completion system that when the token-completer is > invoked, it should use the _tokens function to supply the matches: > > zstyle ':completion:token-completion:*' completer _tokens > > And you're done. If you eventually want it to complete other things, you > can append more functions to the zstyle. > > Another way to do this is to create a file in your $fpath having a name > starting with underscore, and begin that file with a "#compdef -k ..." > line. That could be as simple as this: > > ---- 8< ---- snip ---- 8< ---- > #compdef -k complete-word ^K > > _tokens() { compadd ${(f)"$(<tokenfile)"} } > _generic _tokens > ---- 8< ---- snip ---- 8< ---- > > However, that will redefine the _tokens function on each completion. A > slightly better formulation would be to have the file redefine the same > function as its file name. If the file is named "_token_completion": > > ---- 8< ---- snip ---- 8< ---- > #compdef -k complete-word ^K > > _tokens() { compadd ${(f)"$(<tokenfile)"} } > _token_completion() { _generic _tokens } > _token_completion > ---- 8< ---- snip ---- 8< ---- > > It's probably not necessary to go this far when _tokens is so simple, > but if you eventually write something more involved it's a good pattern. > > Note that with the file you don't need the zstyle: passing _tokens as an > argument to _generic has the equivalent effect. You could omit _tokens > there and instead have the style, just replace "token-completion" in the > style pattern with the name of the file. -- Eric Smith ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: implementing a control for completing filenames with a defined list of tokens 2014-03-16 14:13 ` Eric Smith @ 2014-03-16 19:27 ` Bart Schaefer 2014-03-16 20:13 ` Bart Schaefer 0 siblings, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-16 19:27 UTC (permalink / raw) To: Zsh Users On Mar 16, 3:13pm, Eric Smith wrote: } } I want to use this completion facility at the start of file (a } dir) names and also *in the middle* of the name. } So the tags may appear anywhere in the filename. } So I could type foobar__^K } for example and the function will complete with my tokens which all } begin with a double underscore. Hmm, that's a somewhat different question, and the answer depends on whether you want to complete existing directory names (limit the result to those that match your tokens) or whether you are trying to construct new names from the input line plus the tokens. Limiting to existing names is quite a bit easier; instead of defining a completer for the token-completion generic widget, we just define a file-patterns style: zle -C token-completion complete-word _generic bindkey ^K token-completion zstyle -e ':completion:token-completion:*' file-patterns \ 'reply=( "*(${(j:|:)${(qf)"$(<tokenfile)"}})*(/)" )' I've used zstyle -e there so that tokenfile is re-read every time the patterns are needed, so you can update the tokens without having to reload the completion. Replace tokenfile with a full path unless you want the tokens to be relative to the current directory. What happens there is that the pattern is used to create a list of all possible directories (because of the trailing "(/)" qualifier) that match your tokens, and then the completion system will automatically narrow that list to the ones that also match the current input line. To build up a word for a file that doesn't exist, you need a completer as in the original scheme, but you'll have to paste the prefix/suffix from the command line around the tokens yourself. Something like: _tokens() { if [[ $PREFIX$SUFFIX = *__* ]]; then compadd ${PREFIX%_#}${^${(f)"$(<tokenfile)"}}${SUFFIX#_#} else return 1 fi } where ${PREFIX%_#} means the prefix with any trailing underscores removed, and similarly ${SUFFIX#_#} removes leading underscores. And then you need "setopt complete_in_word" to have this in the middle of the word. If you want to combine both schemes, e.g., complete words that match a file name but offer words that don't if there are no such files, you need something like zle -C token-completion complete-word _generic bindkey ^K token-completion zstyle -e ':completion:token-completion:*' file-patterns \ 'reply=( "*(${(j:|:)${(qf)"$(<tokenfile)"}})*(/)" )' zstyle ':completion:token-completion:*' completer _files _tokens (with _tokens as above). ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: implementing a control for completing filenames with a defined list of tokens 2014-03-16 19:27 ` Bart Schaefer @ 2014-03-16 20:13 ` Bart Schaefer 2014-03-18 3:10 ` set -F kills read -t Ray Andrews 0 siblings, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-16 20:13 UTC (permalink / raw) To: Zsh Users On Mar 16, 12:27pm, Bart Schaefer wrote: } } _tokens() { } if [[ $PREFIX$SUFFIX = *__* ]]; then } compadd ${PREFIX%_#}${^${(f)"$(<tokenfile)"}}${SUFFIX#_#} Apologies, that needs to be ${PREFIX%%_#} and ${SUFFIX##_#} to get all the underscores. ^ permalink raw reply [flat|nested] 29+ messages in thread
* set -F kills read -t 2014-03-16 20:13 ` Bart Schaefer @ 2014-03-18 3:10 ` Ray Andrews 2014-03-18 6:50 ` Bart Schaefer 0 siblings, 1 reply; 29+ messages in thread From: Ray Andrews @ 2014-03-18 3:10 UTC (permalink / raw) To: zsh-users All: I'm trying to get some functions to accept piped input: func () { ... read -t input echo "$input to a summer's day?" ... } $ echo "Shall I compare thee" | func Shall I compare thee to a summer's day? ... All good. However if 'set -F' is active before 'func' is called or even it it's set just before the 'read -t input' then nothing is readed. Why is that? I thought 'set -F' was just to prevent glob expansions. Can it be fixed? Tx. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 3:10 ` set -F kills read -t Ray Andrews @ 2014-03-18 6:50 ` Bart Schaefer 2014-03-18 16:22 ` Ray Andrews 0 siblings, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-18 6:50 UTC (permalink / raw) To: zsh-users On Mar 17, 8:10pm, Ray Andrews wrote: } Subject: set -F kills read -t } } func () } { } ... } read -t input } echo "$input to a summer's day?" } ... } } } } $ echo "Shall I compare thee" | func } } Shall I compare thee to a summer's day? } } ... } All good. However if 'set -F' is active before 'func' is called or even } it it's set just before the 'read -t input' } then nothing is readed. Why is that? I thought 'set -F' was just to } prevent glob expansions. Can it be fixed? I'm not able to reproduce this: zsh -f torch% func() { set -F; read -t input; print "$input to a summer's day?" } torch% echo "Shall I compare thee" | func Shall I compare thee to a summer's day? torch% Also the stuff that you've elided with "..." that comes before "read" may be important. When you pipe to a function, the default standard input of every command in that function is the same as the input of the function itself, so something else could be consuming the input before "read" gets a shot at it. Further, "read -t" means to fail immediately if input is not ready when "read" begins executing. Because zsh forks to the left, there is an inherent race condition in having "read -t" on the right side of a pipeline; the "echo" in the forked subshell may not yet have had a chance to do anything by the time that the "read" in the parent shell examines standard input. Try examining $? after "read -t input" finishes. If it's 1, then the read timed out. If you change to "read -t 1 input" you may find the problem disappears. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 6:50 ` Bart Schaefer @ 2014-03-18 16:22 ` Ray Andrews 2014-03-18 16:47 ` Peter Stephenson 2014-03-18 17:45 ` Bart Schaefer 0 siblings, 2 replies; 29+ messages in thread From: Ray Andrews @ 2014-03-18 16:22 UTC (permalink / raw) To: zsh-users On 03/17/2014 11:50 PM, Bart Schaefer wrote: Bart: Confusions within Confuzzlements. > I'm not able to reproduce this: > > zsh -f > torch% func() { set -F; read -t input; print "$input to a summer's day?" } > torch% echo "Shall I compare thee" | func > Shall I compare thee to a summer's day? > torch% Yeah, that works, but your further comments expose what seems to me to be a bug. > Further, "read -t" means to fail immediately if input is not ready > when "read" begins executing. Because zsh forks to the left, there > is an inherent race condition in having "read -t" on the right side > of a pipeline; the "echo" in the forked subshell may not yet have > had a chance to do anything by the time that the "read" in the parent > shell examines standard input. > > Try examining $? after "read -t input" finishes. If it's 1, then the > read timed out. > > If you change to "read -t 1 input" you may find the problem disappears. Your code: func0() { set -F; read -t input; print "$input to a summer's day?" } And this run: $ s="lowercase s"; S=UPPERCASE S" $ echo "$s $S" lowercase s UPPERCASE S $ echo $S | func0 UPPERCASE S to a summer's day? $ echo $s | func0 lowercase s to a summer's day? $ echo $S | func0 lowercase s to a summer's day? << WRONG! $ echo "$s $S" lowercase s UPPERCASE S $ echo $S | func0 UPPERCASE S to a summer's day? << THAT'S BETTER ... How can such a thing ever be permitted? That's just plain broken. But, from your comments I tried this: (it seems the 'set -F' thing is a red herring) func1() { read -t 1 input; print "$input to a summer's day?" } ... And the same run of tests is fine. I have no idea how this 'race condition' stuff works but surely, whatever "read -t 1" has that "read -t" lacks should be automatic? When/where would my first run above ever be acceptable? It should print the correct variable, or maybe it should fail completely, but printing the wrong variable is just a felony, no? Nothing is more important than predictability. In a pipe situation, 'read' shouldn't leave the station until all the passengers are on board, but even if it does, it shouldn't give my seat to someone else and then call them me. Or so it looks to this grasshopper. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 16:22 ` Ray Andrews @ 2014-03-18 16:47 ` Peter Stephenson 2014-03-18 17:45 ` Bart Schaefer 1 sibling, 0 replies; 29+ messages in thread From: Peter Stephenson @ 2014-03-18 16:47 UTC (permalink / raw) To: zsh-users On Tue, 18 Mar 2014 09:22:22 -0700 Ray Andrews <rayandrews@eastlink.ca> wrote: > I have no idea how this 'race > condition' stuff > works but surely, whatever "read -t 1" has that "read -t" lacks should > be automatic? No, that's the whole point of the difference! "read -t" says "test the input state *right now*. Trust me, I really do mean 'right now'. I am over 18". "read -t 1" says "wait at most second to see if some input turns up". When you start a pipeline, do-stuff-with-some-output | do-stuff-with-some-input it's inevitable that what happens on the left and the right is going to take some time. The two processes are asynchronous by design. The only thing the shell guarantees you is that the pipeline itself will be set up (there is some internal synchronisation to ensure there's something running on the left and something running on the right). When input and output are taking place on the processes on the left and right are down to details of those processes that the shell framework for pipelines simply can't worry about. So to ensure synchronisation of input and output you need to do extra work. The "wait a second" option is the easiest way of doing that, although it's not a guarantee. However, there may be better ways, e.g. you can also tell it "read 1 character, waiting until that arrives". It depends what you're ultimate object is. Welcome to "non-blocking I/O"; you've now been through the first lesson, "don't use non-blocking I/O unless you need to". pws ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 16:22 ` Ray Andrews 2014-03-18 16:47 ` Peter Stephenson @ 2014-03-18 17:45 ` Bart Schaefer 2014-03-18 22:08 ` Ray Andrews 1 sibling, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-18 17:45 UTC (permalink / raw) To: zsh-users To add to what PWS just said ... On Mar 18, 9:22am, Ray Andrews wrote: } } ... And the same run of tests is fine. I have no idea how this 'race } condition' stuff works "Race condition" is a term referring to what happens when two (or more) tasks both wish to use the same resource at the same time, but the resource is only available to one task at a time. Think of restroom stalls at a sports stadium at half time; someone ends up waiting, but you can't tell in advance who will get there first. In your case the writer is a janitor arriving to refill the paper towel dispenser, and the reader has just used the sink and need to dry his hands. The -t option tells the reader that if the towel dispenser is empty, he should immediately go away with his hands still wet. If you instead use -t 1, he waits 1 second to give the janitor a chance to do his job. } $ echo $s | func0 } lowercase s to a summer's day? } } $ echo $S | func0 } lowercase s to a summer's day? << WRONG! The "problem" here is that $input is a global variable, so it remained set across runs of func0. In the second case "read -t" failed (if we had bothered to test $? it would have been 1) so nothing new was put into $input and the old value persisted. You could fix this by declaring "local input;" which would cause input to be reset every time the function is called, but that depends on whether you want to be able to refer to $input after the function is finished. (One could argue that "read" should always erase the parameter to which it was told to write, no matter whether the action of reading succeeds; but that's a different conversation.) } Nothing is more important than predictability. Not always true. The point of the -t option is to tell "read" that it is in fact more important not to wait than it is to be predictable. I suspect that what you really want is the answer to the question "is my standard input a pipe?" and to go do something else if it is not. Using "read -t" gives you an unpredictable answer to that question. Without more context of what your function is meant to do when the input is NOT a pipe, we can't tell you the best way to answer that question, or even whether it's the right question in the first place. -- Barton E. Schaefer ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 17:45 ` Bart Schaefer @ 2014-03-18 22:08 ` Ray Andrews 2014-03-18 23:12 ` Jan Larres 2014-03-19 1:17 ` Bart Schaefer 0 siblings, 2 replies; 29+ messages in thread From: Ray Andrews @ 2014-03-18 22:08 UTC (permalink / raw) To: zsh-users On 03/18/2014 10:45 AM, Bart Schaefer wrote: Peter, Bart: Thanks, now I at least know what was busted. I must tread lightly on this point because zsh has it's own culture, but from the perspective of my C brain, " read -t " ... maybe this, maybe that ... with exactly the same input is hard to accept. It's not very robust. > (One could argue that "read" should always erase the parameter to which > it was told to write, no matter whether the action of reading succeeds; > but that's a different conversation.) I'd say it's almost the nub of this conversation. If, as Peter says, zsh is asynchronous, and that means that process one might or might not be finished before process two, then it seems to me that if there is a failure of some sort, then that should be manifested. Identical runs of identical code should produce identical results, no? Or at least warn you if that isn't going to happen. I appeal to the doctrine of least surprise. It should null the variable first, then a twit like me would at least have some warning that something is amiss. Or it could print " (null) " or something else helpful. > } Nothing is more important than predictability. > > Not always true. The point of the -t option is to tell "read" that it > is in fact more important not to wait than it is to be predictable. Ok, but in the context of a pipe can't we have 'wait for the input that IS coming. Don't wait one second or ten seconds or no seconds, wait until the input arrives. Wait until the first 'echo' has done its thing. Ain't that intuitive? When would one ever want 'read -t' to maybe capture input or maybe not, depending on something unpredictable? echo "a string" | func should send "a string" to func absolutely every time. The very existence of the pipe symbol should say 'wait for it'. Wait for 'echo' to return. > I suspect that what you really want is the answer to the question "is > my standard input a pipe?" and to go do something else if it is not. > Using "read -t" gives you an unpredictable answer to that question. > > Without more context of what your function is meant to do when the > input is NOT a pipe, we can't tell you the best way to answer that > question, or even whether it's the right question in the first place. It's just a wrapper function. In this test case, around 'grep'. I use my wrapper directly but I thought it may as well be able to accept input from a pipe too. But this is a matter of principal. Asynchronous piping seems almost a contradiction. Surely each stage of a chain of pipes has a right to expect linear travel of data. This problem seems never to happen with piped binaries, they don't seem to need to wait some arbitrary number of seconds for input, nor should it happen with functions. Or maybe that just can't be done, I don't know. Anyway, in practical terms 'read -t 1' does the trick. Most of the time. Not on Tuesdays nor when I have a process running in the background that slows things down, but mostly it works ;-) Not meaning to rock the boat of course, zsh does what it does. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 22:08 ` Ray Andrews @ 2014-03-18 23:12 ` Jan Larres 2014-03-19 4:06 ` Ray Andrews 2014-03-19 1:17 ` Bart Schaefer 1 sibling, 1 reply; 29+ messages in thread From: Jan Larres @ 2014-03-18 23:12 UTC (permalink / raw) To: zsh-users On 19/03/14 11:08, Ray Andrews wrote: > On 03/18/2014 10:45 AM, Bart Schaefer wrote: >> Not always true. The point of the -t option is to tell "read" that it >> is in fact more important not to wait than it is to be predictable. > > Ok, but in the context of a pipe can't we have 'wait for the input > that IS coming. Don't wait one second or ten seconds or no seconds, > wait until the input arrives. Wait until the first 'echo' has done its > thing. Ain't that intuitive? When would one ever want 'read -t' to > maybe capture input or maybe not, depending on something > unpredictable? > > echo "a string" | func > > should send "a string" to func absolutely every time. The very > existence of the pipe symbol should say 'wait for it'. Wait for 'echo' > to return. As Peter said this is just normal non-blocking I/O, which is not at all shell-specific let alone zsh-specific. Since you clearly seem to want blocking I/O, why are you using the '-t' argument to begin with? If you just wrote 'read input' it should do exactly what you want. -Jan ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 23:12 ` Jan Larres @ 2014-03-19 4:06 ` Ray Andrews 2014-03-19 5:30 ` Jan Larres 0 siblings, 1 reply; 29+ messages in thread From: Ray Andrews @ 2014-03-19 4:06 UTC (permalink / raw) To: zsh-users On 03/18/2014 04:12 PM, Jan Larres wrote: > On 19/03/14 11:08, Ray Andrews wrote: > As Peter said this is just normal non-blocking I/O, which is not at > all shell-specific let alone zsh-specific. Since you clearly seem to > want blocking I/O, why are you using the '-t' argument to begin with? > If you just wrote 'read input' it should do exactly what you want. -Jan Only because sometimes the input to the function is via arguments, not via pipe. I'm trying to emulate what (say) grep does: ls *.txt | grep some_file vs. grep "grep" * ... and only the '-t' switch seems to work with both forms. Really 'read -t 1' seems perfectly functional, I just wonder about the arbitrariness of a time limit vs. a more robust situation, since in a pipe it seems to me that there is no doubt that input is expected. One should be able to insure that the flow of the code is what one would expect without a 'dumb' pause, I'd say. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 4:06 ` Ray Andrews @ 2014-03-19 5:30 ` Jan Larres 2014-03-19 15:23 ` Ray Andrews 0 siblings, 1 reply; 29+ messages in thread From: Jan Larres @ 2014-03-19 5:30 UTC (permalink / raw) To: zsh-users On 19/03/14 17:06, Ray Andrews wrote: > On 03/18/2014 04:12 PM, Jan Larres wrote: >> On 19/03/14 11:08, Ray Andrews wrote: >> As Peter said this is just normal non-blocking I/O, which is not at >> all shell-specific let alone zsh-specific. Since you clearly seem to >> want blocking I/O, why are you using the '-t' argument to begin with? >> If you just wrote 'read input' it should do exactly what you want. > > Only because sometimes the input to the function is via arguments, not > via pipe. I'm trying to emulate what (say) grep does: > > ls *.txt | grep some_file > > vs. > > grep "grep" * The best way to do that, and presumably the way grep itself does it, is to test whether stdin is connected to a terminal: mygrep() { if [ -t 0 ]; then echo terminal else read input echo $input input fi } $ mygrep terminal $ echo foo | mygrep foo input -Jan ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 5:30 ` Jan Larres @ 2014-03-19 15:23 ` Ray Andrews 2014-03-19 20:00 ` Bart Schaefer 0 siblings, 1 reply; 29+ messages in thread From: Ray Andrews @ 2014-03-19 15:23 UTC (permalink / raw) To: zsh-users On 03/18/2014 10:30 PM, Jan Larres wrote: > The best way to do that, and presumably the way grep itself does it, is > to test whether stdin is connected to a terminal: > > mygrep() { > if [ -t 0 ]; then > echo terminal > else > read input > echo $input input > fi > } > > $ mygrep > terminal > $ echo foo | mygrep > foo input > Bloody marvellous! That scratches my itch just perfectly, thanks Jan. I wish there was some web site: "101 things you still don't know you can do with zsh, and why you want to know them". ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 15:23 ` Ray Andrews @ 2014-03-19 20:00 ` Bart Schaefer 2014-03-20 1:47 ` Ray Andrews 0 siblings, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-19 20:00 UTC (permalink / raw) To: zsh-users On Mar 19, 8:23am, Ray Andrews wrote: } Subject: Re: set -F kills read -t } } On 03/18/2014 10:30 PM, Jan Larres wrote: } > The best way to do that, and presumably the way grep itself does it, is Grep doesn't care if the input is a terminal, it only cares whether it has a file name in its argument list or not. } I wish there was some web site: "101 things you still don't know you } can do with zsh, and why you want to know them". http://www.amazon.com/Bash-Shell-Conquering-Command-Line/dp/1590593766/ref=sr_1_1?s=books&ie=UTF8&qid=1395259194&sr=1-1&keywords=from+bash+to+z+shell :-) ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 20:00 ` Bart Schaefer @ 2014-03-20 1:47 ` Ray Andrews 0 siblings, 0 replies; 29+ messages in thread From: Ray Andrews @ 2014-03-20 1:47 UTC (permalink / raw) To: zsh-users On 03/19/2014 01:00 PM, Bart Schaefer wrote: > On Mar 19, 8:23am, Ray Andrews wrote: > } Subject: Re: set -F kills read -t > } > } On 03/18/2014 10:30 PM, Jan Larres wrote: > } > The best way to do that, and presumably the way grep itself does it, is > > Grep doesn't care if the input is a terminal, it only cares whether it > has a file name in its argument list or not. > > } I wish there was some web site: "101 things you still don't know you > } can do with zsh, and why you want to know them". > > http://www.amazon.com/Bash-Shell-Conquering-Command-Line/dp/1590593766/ref=sr_1_1?s=books&ie=UTF8&qid=1395259194&sr=1-1&keywords=from+bash+to+z+shell > > :-) > I'm on chapter two ;-) ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-18 22:08 ` Ray Andrews 2014-03-18 23:12 ` Jan Larres @ 2014-03-19 1:17 ` Bart Schaefer 2014-03-19 5:00 ` Ray Andrews 1 sibling, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-19 1:17 UTC (permalink / raw) To: zsh-users Pardon me while I provide further evidence in support of the theorem that the best way to get the correct answer from the internet is to post the wrong answer. On Mar 18, 3:08pm, Ray Andrews wrote: } } Thanks, now I at least know what was busted. I must tread lightly on } this point because zsh has it's own culture, but from the perspective } of my C brain, " read -t " ... maybe this, maybe that ... with exactly } the same input is hard to accept. It's not very robust. "read -t input" in zsh is pretty nearly equivalent to this C code: char input[1024]; fcntl(0, F_SETFL, O_NONBLOCK); read(0, input, 1024); (except of course that the size of the input is not predetermined). If you cojole that C into a runnable program and try it, you'll find that it behaves just the way "read -t" does. If you don't want that fcntl() in there, don't pass the -t option. } > (One could argue that "read" should always erase the parameter to which } > it was told to write, no matter whether the action of reading succeeds; } > but that's a different conversation.) } I'd say it's almost the nub of this conversation. If, as Peter says, } zsh is asynchronous, and that means that process one might or might } not be finished before process two, then it seems to me that if there } is a failure of some sort, then that should be manifested. It *IS* manifested ... as the return value of "read" ($?). Which your function ignored ... The full situation goes something like this: input=START if read -t input then print "I definitely read input: $input" elif (( $#input )) then print "Error, input unchanged: $input" else print "End of file: $input" fi In most cases you don't care: if read -t input then print "got \$input: $input" else print "got nothing, do not use \$input" fi } Identical runs of identical code should produce identical results, no? No. Consider for example: for (( count=1; count <= 10000; count++ )) do if read -t something then print "GOT: $something"; break fi print $count done This happily counts to 10000, stopping as soon as it gets input. Exactly how many numbers are printed before it stops depends on when the input arrives. Identical runs of identical code, but not identical results. If the most important thing is watching it count, then this is exactly what "read -t" is for. If the important thing is "GOT: $something", then don't use -t. } Or at least warn you if that isn't going to happen. I appeal to the } doctrine of least surprise. I appeal to the documentation: -t [ NUM ] Test if input is available BEFORE ATTEMPTING TO READ. ... If no input is available, return status 1 AND DO NOT SET ANY VARIABLES. (emphasis mine, obviously). What is it that made you believe you need the -t option in the first place? } Ok, but in the context of a pipe can't we have 'wait for the input } that IS coming. Don't wait one second or ten seconds or no seconds, } wait until the input arrives. Well, yes. That's what "read" *without* the -t option does. That's why I said: } > I suspect that what you really want is the answer to the question "is } > my standard input a pipe?" and to go do something else if it is not. What is it about piped input that requires different behavior from your function? } echo "a string" | func } } should send "a string" to func absolutely every time. It does send it. Whether "func" consumes that input is up to the code inside of "func". } The very existence of the pipe symbol should say 'wait for it'. Wait } for 'echo' to return. But that's not what the pipe symbol means. It means only "connect the standard output of (stuff on the left) to the standard input of (stuff on the right)." Consider what would happen if "echo" produced a gigabyte of output, or a terabyte, or a petabyte. Where is all of that supposed to go while waiting for echo to return? Do you always expect your web browser to download an entire video before beginning to play it? } It's just a wrapper function. In this test case, around 'grep'. OK, but the wrapper must be doing *something*. I mean, this is a wrapper around grep: mygrep() { print -u2 "Hi, I'm going to grep now." grep "$@" } This will perfectly happily read from a pipe, a file, or a file named in the arguments, without needing "read" at all. So what exactly is your wrapper doing, such that it needs to consume standard input before grep does? } But this is a matter of principal. Asynchronous piping seems almost a } contradiction. Every shell since the 1970s has worked this way, so you may want to reconsider which principle is involved. :-) } Surely each stage of a chain of pipes has a right to expect linear } travel of data. If you think of a pipeline as establishing "direction", then yes, the chain has the right to expect that the data will always flow in the same direction and (if there is only a single producer) in a fixed order; but it does not have the right to expect that it will always start flowing at a particular time and flow at a fixed rate. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 1:17 ` Bart Schaefer @ 2014-03-19 5:00 ` Ray Andrews 2014-03-19 6:37 ` Bart Schaefer 2014-03-19 10:00 ` Roman Neuhauser 0 siblings, 2 replies; 29+ messages in thread From: Ray Andrews @ 2014-03-19 5:00 UTC (permalink / raw) To: zsh-users On 03/18/2014 06:17 PM, Bart Schaefer wrote: Bart, > Pardon me while I provide further evidence in support of the theorem that > the best way to get the correct answer from the internet is to post the > wrong answer. Who's answer was wrong? > (except of course that the size of the input is not predetermined). If > you cojole that C into a runnable program and try it, you'll find that > it behaves just the way "read -t" does. If you don't want that fcntl() > in there, don't pass the -t option. > Ok, then how else would one write a function that could use arguments *or* piped input? grep does it, and doesn't need an arbitrary wait. Again, in *practice* the " -t 1 " seems perfectly good enough, I don't want to belabour this, it's a theoretical/philosophical point only. > > input=START > if read -t input > then print "I definitely read input: $input" > elif (( $#input )) > then print "Error, input unchanged: $input" > else print "End of file: $input" > fi > Ok, that at least covers the bases if the read failed > } Identical runs of identical code should produce identical results, no? > > No. Consider for example: > > for (( count=1; count <= 10000; count++ )) do > if read -t something > then print "GOT: $something"; break > fi > print $count > done > > This happily counts to 10000, stopping as soon as it gets input. Exactly > how many numbers are printed before it stops depends on when the input > arrives. Identical runs of identical code, but not identical results. > If the most important thing is watching it count, then this is exactly > what "read -t" is for. If the important thing is "GOT: $something", > then don't use -t. Point made. That's well and good, I can see that " -t " would be exactly right in that situation, but in my pipe situation, it's not exactly right. > } Or at least warn you if that isn't going to happen. I appeal to the > } doctrine of least surprise. > > I appeal to the documentation: > > -t [ NUM ] > Test if input is available BEFORE ATTEMPTING TO READ. ... > If no input is available, return status 1 AND DO NOT SET > ANY VARIABLES. Yeah ... I guess in my still synchronous and 'blocking' mind, when there's a pipe there is *always* input so always 'available'. It will take some getting used to the idea that the various steps in a long sequence of piped functions can quit any time they like in any order that happens to happen. > > Consider what would happen if "echo" produced a gigabyte of output, or > a terabyte, or a petabyte. Where is all of that supposed to go while > waiting for echo to return? Do you always expect your web browser to > download an entire video before beginning to play it? A valid point. I have no issue that " -t [seconds to wait] " is available when needed, but in the case of: echo "a string" | func I hardly think that func should not politely wait for "a string", and as my tests showed, sometimes it didn't. I dunno, maybe there is no way that 'read' can be aware that something to the left of it in a pipe situation has returned, so what I'm wanting might be impossible. If it was possible it would be the '-p' switch: 'pipe mode' ... wait for piped input to finish. > } It's just a wrapper function. In this test case, around 'grep'. > > OK, but the wrapper must be doing *something*. I mean, this is a > wrapper around grep: > > mygrep() { > print -u2 "Hi, I'm going to grep now." > grep "$@" > } > > This will perfectly happily read from a pipe, a file, or a file named > in the arguments, without needing "read" at all. So what exactly is > your wrapper doing, such that it needs to consume standard input before > grep does? Yabut, in the pipe situation you don't supply a filespec .... Shoot. All this 'read' stuff has been a colossal mistake. Damn, everything I read on the internet said 'read' was the way to go. HAAA, which get's back to your theorem! I see now how I've been barking up the wrong tree. It never even occurred to me that " $@ " would soak up piped input, I thought " $@ " stuff had to be arguments after the command <:-( Sorry Bart, I'm a long study. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 5:00 ` Ray Andrews @ 2014-03-19 6:37 ` Bart Schaefer 2014-03-19 17:08 ` Ray Andrews 2014-03-19 10:00 ` Roman Neuhauser 1 sibling, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-19 6:37 UTC (permalink / raw) To: zsh-users On Mar 18, 10:00pm, Ray Andrews wrote: } } Yeah ... I guess in my still synchronous and 'blocking' mind, when } there's a pipe there is *always* input so always 'available'. Consider this silly example: cat /dev/null | func There will be no output from cat, so as seen by [the commands in the definition of] func, there's a pipe, but there is no input. } ... in the case of: } } echo "a string" | func } } I hardly think that func should not politely wait for "a string" Shell functions are little more than names for the set of commands inside them; it's nonsensical to say that "func should politely wait". What will politely wait (or not) is the first command inside func that attempts to read standard input. If there is no such command, nothing waits. } > mygrep() { } > print -u2 "Hi, I'm going to grep now." } > grep "$@" } > } } } ... I see now how I've been barking up the wrong tree. It never even } occurred to me that " $@ " would soak up piped input, I thought " $@ " } stuff had to be arguments after the command <:-( It's not actually the case that $@ "soaks up" anything. mygrep pattern file expands to { print -u2 "Hi, I'm going to grep now." grep "pattern" "file" } so grep reads from the file named "file", whereas mygrep pattern expands to { print -u2 "Hi, I'm going to grep now." grep "pattern" } and then grep, noting that it has only a pattern and no file name, reads from standard input just like it always does. If there's any magic here at all, it's that the standard input of mygrep and the standard input of grep are the same pipe. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 6:37 ` Bart Schaefer @ 2014-03-19 17:08 ` Ray Andrews 2014-03-19 17:22 ` Roman Neuhauser 2014-03-19 22:21 ` Bart Schaefer 0 siblings, 2 replies; 29+ messages in thread From: Ray Andrews @ 2014-03-19 17:08 UTC (permalink / raw) To: zsh-users [-- Attachment #1: Type: text/plain, Size: 3013 bytes --] On 03/18/2014 11:37 PM, Bart Schaefer wrote: > cat /dev/null | func > > There will be no output from cat, so as seen by [the commands in the > definition of] func, there's a pipe, but there is no input. Yabut 'cat' still returns, so I'd use that as the terminator. echo "" | func ... would 'know' that echo is 'finished' and that's that. Can't one process know that another process is finished? Anyway it doesn't matter, Jan's solution is crisp, understandable, robust and has no ifs, buts or maybes. As you said, this asynchronous stuff is bedrock to shell and it ain't going to change, I hafta get used to it. > > Shell functions are little more than names for the set of commands > inside them; it's nonsensical to say that "func should politely wait". > What will politely wait (or not) is the first command inside func > that attempts to read standard input. If there is no such command, > nothing waits. Of course. One can't presume that the function has some behaviours apart from the behaviours of it's contents. But in this case we're referring to 'read' and I had speculated that 'read' might be able to have the option of being asked to wait for some previous command to finish. Come to think of it, what it would do is take Jan's code and more or less bundle that into an option to 'read'. Nevermind: if [ ! -t 0 ]; then read input; fi ... isn't exactly hard to type. I yield the point. > > It's not actually the case that $@ "soaks up" anything. > > and then grep, noting that it has only a pattern and no file name, reads > from standard input just like it always does. I see. Thanks for pointing that out, I was about to replace ignorance with false understanding. I thought I was expecting this to work: $ y () { echo "$1, $2, $3 "; } $ y one two three one, two, three $ echo "one two three" | y , , << :-( So grep is doing the 'switcheroo' here--going from reading arguments, to reading stdin, and the pipe is the de facto 'stdin', and piped input does NOT become arguments :-) Got it. I'm doing this sort of thing now, and I'm as happy as a clam: function y () { pipeinput='(nothing in the pipe)' terminalinput='(nothing from the terminal)' if [ ! -t 0 ]; then read pipeinput; fi if [ -n "$1" ]; then terminalinput="$@"; fi echo "$pipeinput $terminalinput" } $ y "from an antique land" (nothing in the pipe) from an antique land $ echo "I met a traveller" | y I met a traveller (nothing from the terminal) $ echo "I met a traveller" | y "from an antique land" I met a traveller from an antique land $ echo "I met a traveller" | y "from an antique land" | y "\nWho said: \"Two vast and trunkless legs of stone" I met a traveller from an antique land Who said: "Two vast and trunkless legs of stone ... Who could ask for more transparent code? Thanks all for your patience. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 17:08 ` Ray Andrews @ 2014-03-19 17:22 ` Roman Neuhauser 2014-03-19 22:21 ` Bart Schaefer 1 sibling, 0 replies; 29+ messages in thread From: Roman Neuhauser @ 2014-03-19 17:22 UTC (permalink / raw) To: Ray Andrews; +Cc: zsh-users # rayandrews@eastlink.ca / 2014-03-19 10:08:44 -0700: > I thought I was expecting this to work: > > $ y () { echo "$1, $2, $3 "; } > > $ y one two three > one, two, three > > $ echo "one two three" | y > , , << :-( what would you expect to happen in $ echo "one two three" | y foo bar baz > So grep is doing the 'switcheroo' here--going from reading arguments, to > reading stdin, and the pipe is the de facto 'stdin', and piped input > does NOT become arguments :-) de facto? stdin is stdin no matter what 'file' the descriptor names. -- roman ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 17:08 ` Ray Andrews 2014-03-19 17:22 ` Roman Neuhauser @ 2014-03-19 22:21 ` Bart Schaefer 2014-03-20 1:46 ` Ray Andrews 1 sibling, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-19 22:21 UTC (permalink / raw) To: zsh-users On Mar 19, 10:08am, Ray Andrews wrote: } Subject: Re: set -F kills read -t } } } On 03/18/2014 11:37 PM, Bart Schaefer wrote: } > cat /dev/null | func } > } > There will be no output from cat, so as seen by [the commands in the } > definition of] func, there's a pipe, but there is no input. } } Yabut 'cat' still returns, so I'd use that as the terminator. In the general case, though, you don't know *when* the thing on the left will be done. Yes, end-of-file can be detected on the pipe, but: { sleep $((RANDOM * 1000)); cat /dev/null } | func The default behavior of just about everything *except* "read -t" will be for func to wait forever to for the pipe to close. I'm still curious what put you on to "-t" in the first place. } But in this case we're referring to 'read' and I had speculated that } 'read' might be able to have the option of being asked to wait for } some previous command to finish. Another thing you may be missing here is that "read" consumes ONE LINE of text: A string ending in "\n" (or end of file). If you do not use "-t", then it waits as long as it must in order to gobble up one line. But it won't wait for a second line. (Of course you can tell it that something other than a "\n" should be used as a the line ending, in which case it may very well swallow everything up to end-of-file on the pipe, but that requires even more fooling around and -t has you quite well enough confused already.) } function y () } { } pipeinput='(nothing in the pipe)' } terminalinput='(nothing from the terminal)' } if [ ! -t 0 ]; then read pipeinput; fi } if [ -n "$1" ]; then terminalinput="$@"; fi } echo "$pipeinput $terminalinput" } } It's a little odd to call $@ the "terminal input" -- you can have stdin come from a tty device the same as from any other file. All that the above has said is that you never want to read from a tty, but you're willing to read exactly one line from anywhere else. Consider: $ y 'I met a man with seven wives' <<<'As I was going to St Ives' As long as your clams are happy, though ... ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 22:21 ` Bart Schaefer @ 2014-03-20 1:46 ` Ray Andrews 2014-03-20 4:21 ` Bart Schaefer 0 siblings, 1 reply; 29+ messages in thread From: Ray Andrews @ 2014-03-20 1:46 UTC (permalink / raw) To: zsh-users On 03/19/2014 03:21 PM, Bart Schaefer wrote: > I'm still curious what put you on to "-t" in the first place. I was just Googling for advice on how to get a function to accept piped input and 'read -t' came up several times. Monkey see, monkey do. > Another thing you may be missing here is that "read" consumes ONE LINE > of text: A string ending in "\n" (or end of file). If you do not use > "-t", then it waits as long as it must in order to gobble up one line. > But it won't wait for a second line. Yes, I know that. But it's a place to start. > (Of course you can tell it that something other than a "\n" should be > used as a the line ending, in which case it may very well swallow > everything up to end-of-file on the pipe, but that requires even more > fooling around and -t has you quite well enough confused already.) > > } function y () > } { > } pipeinput='(nothing in the pipe)' > } terminalinput='(nothing from the terminal)' > } if [ ! -t 0 ]; then read pipeinput; fi > } if [ -n "$1" ]; then terminalinput="$@"; fi > } echo "$pipeinput $terminalinput" > } } > > It's a little odd to call $@ the "terminal input" -- you can have stdin > come from a tty device the same as from any other file. All that the > above has said is that you never want to read from a tty, but you're > willing to read exactly one line from anywhere else. Consider: > > $ y 'I met a man with seven wives' <<<'As I was going to St Ives' > > As long as your clams are happy, though ... Now don't go confusin' me again ;-) Hmmm ... no, you're right, it should be able to accept any volume of input as a matter of principal. Ok Bart, how would you write it? The above is a bridgehead in as much as it accepts input from either end, but how could it be improved? I tried 'cat' with no luck. I've already done some chains of pipes (using binaries) up to maybe half a dozen steps, and you just don't have to worry about time-outs, or single lines or anything at all, it just works, even on Tuesday. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-20 1:46 ` Ray Andrews @ 2014-03-20 4:21 ` Bart Schaefer 2014-03-20 15:49 ` Ray Andrews 0 siblings, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-20 4:21 UTC (permalink / raw) To: zsh-users On Mar 19, 6:46pm, Ray Andrews wrote: } } Ok Bart, how would you write it? The above is a bridgehead in as much } as it accepts input from either end Command line arguments are a fundamentally different kind of "input". Files, pipes, and terminals are often called "I/O streams" because data flows from or to them like water (same analogy from which "pipes" is derived). Command line arguments are fixed locations in memory (the argv[] array in C programs) that may be populated at the time the program starts; nothing "flows" in the arguments. So it's better not to think of this as "input from either end." They are entirely distinct. Because the command line arguments are fixed at program start, they're often used to control the behavior. Using the example of "grep" (and temporarily ignoring option flags), the first argument is the pattern to look for, and *if* there is a second (and so on) argument, it's the name of the file to look in, so grep opens that file. Otherwise it uses stdin, but either way it has an I/O stream in which to look for the pattern. The arguments were only used to control the choice of stream. Grep would be much less useful if treated command line arguments as if they were file *contents* instead of file *names*. That's not to say one never treats a command line argument as content, but it's not the typical usage. } but how could it be improved? I tried 'cat' with no luck. I'm still not sure I understand exactly what you want to do, but let's look at a fragment of "man cat" as an example: cat [OPTION] [FILE]... Concatenate FILE(s), or standard input, to standard output. With no FILE, or when FILE is -, read standard input. (This manpage uses FILE as both the name of the file and as shorthand for "the contents of FILE", which is common.) Note that "cat" can actually read standard input MORE THAN ONCE! It tries again every time it finds "-" as a file name. Most of the time you'll just get end of file after the first try, but some special kinds of streams (which includes terminals) can be closed and then opened again "where they left off" (so to speak). The equivalent documentation for your "y" function with [[ ! -t 0 ]] might read: y [STRING]... If the input is not a terminal, copy one LINE from standard input, otherwise LINE is empty. Concatenate LINE and STRING(s), to standard output. If that's how you meant for it to work, I can't improve on what you have, except for some cosmetics (for example, you should not test [ -n "$1" ] because it's possible for $1 to be empty and $2 to have something in it). If you want to copy the entire standard input and then append the STRING(s) at the end of it, it should be fine to do: function y () { if [ ! -t 0 ]; then cat else echo '(nothing in the pipe)'; fi if [ $# -gt 0 ]; then print -r -- "$*" else echo '(nothing in the args)'; fi } Not *exactly* as before since I've assumed that if you'd be copying multiple lines from the input then it's OK to put a newline after the '(nothing in the pipe)' string. The "print -r --" is more cosmetics, so that "y -n" will output the -n instead of suppressing the newline from echo. If you meant for "y" to accept the options of echo, by all means change that back. } I've already done some chains of pipes (using binaries) up to maybe } half a dozen steps, and you just don't have to worry about time-outs, } or single lines or anything at all, it just works, even on Tuesday. Yes, but have you noticed that if you just type at the command line % cat (or pretty much anything else in your chain of pipes) that it will just sit there forever waiting for something? It's reading the terminal, because that's what the shell tells it is the standard input. That's what all those programs that "just work" doing: an unconditional, blocking-forever, read from somewhere, stdin by default. As soon as you throw in the bit about not waiting on the terminal when there is no pipe on the left, you have to start worrying about a lot of the stuff that you didn't worry about before. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-20 4:21 ` Bart Schaefer @ 2014-03-20 15:49 ` Ray Andrews 2014-03-20 16:08 ` Bart Schaefer 0 siblings, 1 reply; 29+ messages in thread From: Ray Andrews @ 2014-03-20 15:49 UTC (permalink / raw) To: zsh-users [-- Attachment #1: Type: text/plain, Size: 2633 bytes --] On 03/19/2014 09:21 PM, Bart Schaefer wrote: > On Mar 19, 6:46pm, Ray Andrews wrote: > } > } Ok Bart, how would you write it? The above is a bridgehead in as much > } as it accepts input from either end > > Command line arguments are a fundamentally different kind of "input". Yeah, I know. I'm speaking in a sloppy way because I'm focusing in on the 'grep' situation where the command can be fed from either end. But of course it seems that that is particular to grep, not zsh syntax itself, so you're right to suspect my understanding. > > I'm still not sure I understand exactly what you want to do, This! : function y () { if [ ! -t 0 ]; then cat else echo '(nothing in the pipe)'; fi if [ $# -gt 0 ]; then print -r -- "$*" else echo '(nothing in the args)'; fi } Except that we've come in a circle. Try calling it from this alias: alias y='set -F; _y' function _y () { if [ ! -t 0 ]; then cat else echo '(nothing in the pipe)'; fi if [ $# -gt 0 ]; then print -r -- "$*" else echo '(nothing in the args)'; fi } .... back to 'set -F' killing the pipe :-( $ echo "My love is" | _y "as a fever" My love is as a fever $ echo "My love is" | y "as a fever" (nothing in the pipe) as a fever > If that's how you meant for it to work, I can't improve on what you > have, except for some cosmetics (for example, you should not test > [ -n "$1" ] because it's possible for $1 to be empty and $2 to have > something in it). That's a rude shock but I just tested it, and right you are. Anyway '[ $# -gt 0 ]' is obviously robust, so I like it. > (or pretty much anything else in your chain of pipes) that it will just > sit there forever waiting for something? It's reading the terminal, > because that's what the shell tells it is the standard input. That's > what all those programs that "just work" doing: an unconditional, > blocking-forever, read from somewhere, stdin by default. Yes! And that's how God meant it to be ;-) > As soon as you throw in the bit about not waiting on the terminal when > there is no pipe on the left, you have to start worrying about a lot > of the stuff that you didn't worry about before. Misericordia Bart, I'm taking too much of your time. The list functions at a higher level than my novice problems. I'm dabbling in sacred mysteries that are above me. Let me get back to Peter's book and throw myself at this stuff when I know a bit more Coptic. C is meant to be understandable and predictable, the shells are meant to be like playing Dungeons and Dragons. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-20 15:49 ` Ray Andrews @ 2014-03-20 16:08 ` Bart Schaefer 2014-03-20 21:27 ` Ray Andrews 0 siblings, 1 reply; 29+ messages in thread From: Bart Schaefer @ 2014-03-20 16:08 UTC (permalink / raw) To: zsh-users On Mar 20, 8:49am, Ray Andrews wrote: } } Except that we've come in a circle. Try calling it from this alias: } } alias y='set -F; _y' } } .... back to 'set -F' killing the pipe :-( It's not the "set" that's killing the pipe, it's the semicolon. Didn't we go through this once before? } $ echo "My love is" | y "as a fever" The alias expands that into echo "My love is" | set -F; _y "as a fever" so the semicolon ends the pipeline. Aliases are textual replacements, not semantic units. Also with that alias the -F setting will persist after _y is finished, which I'm sure you didn't intend. Try it this way: alias y='noglob _y' ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-20 16:08 ` Bart Schaefer @ 2014-03-20 21:27 ` Ray Andrews 0 siblings, 0 replies; 29+ messages in thread From: Ray Andrews @ 2014-03-20 21:27 UTC (permalink / raw) To: zsh-users On 03/20/2014 09:08 AM, Bart Schaefer wrote: > echo "My love is" | set -F; _y "as a fever" > > so the semicolon ends the pipeline. Yup, I should have figgered that myself. Red herrings! a dude can be following the wrong scent completely. > Aliases are textual replacements, > not semantic units. Also with that alias the -F setting will persist > after _y is finished, which I'm sure you didn't intend. No, it gets turned off automatically elsewhere in my Grand Unified Wrapper System. > > Try it this way: > > alias y='noglob _y' Seems fine! Thanks Bart, you're a patient man. No more torments for a while anyway. ^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: set -F kills read -t 2014-03-19 5:00 ` Ray Andrews 2014-03-19 6:37 ` Bart Schaefer @ 2014-03-19 10:00 ` Roman Neuhauser 1 sibling, 0 replies; 29+ messages in thread From: Roman Neuhauser @ 2014-03-19 10:00 UTC (permalink / raw) To: Ray Andrews; +Cc: zsh-users # rayandrews@eastlink.ca / 2014-03-18 22:00:49 -0700: > Ok, then how else would one write a function that could use arguments > *or* piped input? repeating another post on this thread: mygrep() { if [ -t 0 ]; then echo terminal else read input echo $input input fi } -- roman ^ permalink raw reply [flat|nested] 29+ messages in thread
end of thread, other threads:[~2014-03-20 21:27 UTC | newest] Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2013-12-02 14:26 implementing a control for completing filenames with a defined list of tokens Eric Smith 2013-12-02 15:58 ` Bart Schaefer 2014-03-16 14:13 ` Eric Smith 2014-03-16 19:27 ` Bart Schaefer 2014-03-16 20:13 ` Bart Schaefer 2014-03-18 3:10 ` set -F kills read -t Ray Andrews 2014-03-18 6:50 ` Bart Schaefer 2014-03-18 16:22 ` Ray Andrews 2014-03-18 16:47 ` Peter Stephenson 2014-03-18 17:45 ` Bart Schaefer 2014-03-18 22:08 ` Ray Andrews 2014-03-18 23:12 ` Jan Larres 2014-03-19 4:06 ` Ray Andrews 2014-03-19 5:30 ` Jan Larres 2014-03-19 15:23 ` Ray Andrews 2014-03-19 20:00 ` Bart Schaefer 2014-03-20 1:47 ` Ray Andrews 2014-03-19 1:17 ` Bart Schaefer 2014-03-19 5:00 ` Ray Andrews 2014-03-19 6:37 ` Bart Schaefer 2014-03-19 17:08 ` Ray Andrews 2014-03-19 17:22 ` Roman Neuhauser 2014-03-19 22:21 ` Bart Schaefer 2014-03-20 1:46 ` Ray Andrews 2014-03-20 4:21 ` Bart Schaefer 2014-03-20 15:49 ` Ray Andrews 2014-03-20 16:08 ` Bart Schaefer 2014-03-20 21:27 ` Ray Andrews 2014-03-19 10:00 ` Roman Neuhauser
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).