From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 12778 invoked by alias); 7 Dec 2015 13:09:40 -0000 Mailing-List: contact zsh-users-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Users List List-Post: List-Help: X-Seq: 21064 Received: (qmail 27361 invoked from network); 7 Dec 2015 13:09:37 -0000 X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM, T_DKIM_INVALID autolearn=ham autolearn_force=no version=3.4.0 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yandex.ru; s=mail; t=1449493128; bh=5HM5uR0mphevSIXtuEbpI63oLZi2gu5N9s6M/Oj9b98=; h=From:To:In-Reply-To:References:Subject:Date; b=MTWubMVgcStrJ9GjC+h3YcJQ9Kqt/IlANBBBuuIXIKfqsKYFhiTA0uX1NVmp4HRbv ORhos9Ay4NU8WkAc/sUk8IfCJ5RrdAZ8NaVeCe6jRSqzjm+32dwltJqJtEaSbv+aJS NckYfIX2P2sw6l5S2F21ss4fj5LiR/SOBG1JEzeQ= From: "Nikolay Aleksandrovich Pavlov (ZyX)" To: "vogt@linux.vnet.ibm.com" , Zsh Users In-Reply-To: <546281449489976@web12j.yandex.ru> References: <20151207105622.GA18231@linux.vnet.ibm.com> <20151207112354.5d24de89@pwslap01u.europe.root.pri> <20151207113941.GA24545@linux.vnet.ibm.com> <531031449489824@web12j.yandex.ru> <546281449489976@web12j.yandex.ru> Subject: Re: Filtering argument lists (e.g. for grep) MIME-Version: 1.0 Message-Id: <575141449493127@web26h.yandex.ru> X-Mailer: Yamail [ http://yandex.ru ] 5.0 Date: Mon, 07 Dec 2015 15:58:47 +0300 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset=utf-8 07.12.2015, 15:18, "Nikolay Aleksandrovich Pavlov (ZyX)" : >  07.12.2015, 15:03, "Nikolay Aleksandrovich Pavlov (ZyX)" : >>   07.12.2015, 14:51, "Dominik Vogt" : >>>    On Mon, Dec 07, 2015 at 11:23:54AM +0000, Peter Stephenson wrote: >>>>     On Mon, 7 Dec 2015 11:56:22 +0100 >>>>     Dominik Vogt wrote: >>>>     > Maybe grep is a bad example because this can be done with the >>>>     > --exclude= option. But could zsh help filtering the names >>>>     > generated by globbing in a more general way so that I could write >>>>     > >>>>     > $ * >>>>     > >>>>     > and have zsh automagically filter the results of the * (not >>>>     > everywhere; only for commands that have this feature enabled) so >>>>     > that the non-matching names are not passed to the command in the >>>>     > first place? >>> >>>>     You could use a global alias, e.g. >>>> >>>>     alias -g '@*'='*~(*\~|\#*|ChangeLog)' >>> >>>    Yes, but then I'd need an alias for every potential pattern, e.g. >>>    @*.s*, @**/*, @*.c.* etc. >>> >>>>     Ig you want that first * to be something more flexible you can use a >>>>     glob qualifier. >>>> >>>>       gi () { >>>>         [[ $REPLY != (*\~|\#*|ChangeLog) ]] >>>>       } >>>> >>>>     and use >>>> >>>>        *(+gi) >>> >>>    That sounds good, but is there a way to make that qualifier a >>>    default for certain commands? As an alternative, is it possible >>>    to access the command name from inside the qualifier function? >>> >>>      function gi () { >>>        if ; then >>>          [[ $REPLY != (*\~|\#*|ChangeLog) ]] >>>        fi >>>      } >> >>   And there is another possibility: considering you want to do this thing with command `foo` you need to do the following: >> >>   1. Create an alias `foo='noglob foo'`. >>   2. Create a function `foo` like this: >> >>           function foo() >>           { >>               local -a args=( "${@[@]}" ) >>               local -a new_args >>               for (( I=2; I<= $#args; I++ )) ; do >>                   if [[ $args[I] != ${${args[I]}//[*?]} ]] ; then # If argument contains glob pattern >>                       args[I]+="(+gi)" >>                       new_args=( $~args[I] ) >>                       args[I,I]=( $new_args ) >>                       (( I += #new_args - 1 )) >>                   fi >>               done >>               command foo "${args[@]}" >>           } >> >>       . I.e. in place of leaving zsh to expand globs, expand it in your function “manually”, with necessary additions. > >  Though this variant is for one command. For multiple you need some adjustments: > >  1. `alias foo='noglob filterglob foo'` >  2. Function is `filterglob`, starts with `local -r cmd="$1"; shift`, ends with `command "$cmd" "${args[@]}"`. > >  And I should not have used `I=2` (it initially meant to skip command) in any case, replace `I=2` with `I=1`. I have checked and the following filterglob function works: filterglob () { local -r cmd="$1" shift local -a args=("${@[@]}") local -a new_args for ((I=1; I<=$#args; I++ )) do if [[ $args[I] != ${${args[I]}/[*?]} ]] then args[I]+="~*.png" new_args=($~args[I]) args[I,I]=("${new_args[@]}") (( I += #new_args - 1 )) fi done "$cmd" "${args[@]}" } . But there is one downside: you need additional code to handle CSH_NULL_GLOB, NULL_GLOB and NOMATCH options. There are the following combinations: 1. nonomatch, nocshnullglob, nonullglob: leave pattern as-is. Requires 1. adding (N) at the end of the pattern 2. reverting args[I] change. (Note: *.c literally is also possible file name, so you can’t just “check if #new_args == 1 and new_args[1] == args[I] and if yes revert”, (N) is needed.) 2. cshnullglob, nonullglob: if there are no matches, leave no arguments, but if all patterns on the command-line have no matches, error out. Requires 1. adding (N) (or it will error out when processing new_args) 2. saving 1 if there were expanded globs. 3. nullglob: should work as-is, including when variant with (N) is used. 4. nomatch: should also work as-is, but when (N) is used requires additionally explicitly errorring out. The final function works something like this: filterglob () { local -r cmd="$1" shift local -a args args=( "${@[@]}" ) local -a new_args local -i expandedglobs=0 local first_unexpanded_glob= for ((I=1; I<=$#args; I++ )) do if [[ $args[I] != ${${args[I]}/[*?]} ]] then local initial_arg=${args[I]} args[I]+="~*.png(N)" new_args=( $~args[I] ) if (( $#new_args )) ; then expandedglobs=1 else if [[ $options[cshnullglob] == off && $options[nullglob] == off ]] ; then if [[ $options[nomatch] == on ]] ; then : ${~${args[I]%\(N\)}} # Will error out. else new_args=( "$initial_arg" ) fi fi if [[ -z $first_unexpanded_glob ]] ; then first_unexpanded_glob=${args[I]%\(N\)} readonly first_unexpanded_glob fi fi args[I,I]=( "${new_args[@]}" ) (( I += $#new_args - 1 )) fi done if [[ $options[cshnullglob] == on && $options[nullglob] == off ]] ; then if (( !expandedglob )) ; then : $~first_unexpanded_glob # Will error out. fi fi "$cmd" "${args[@]}" } . Note that this function has no `emulate -L zsh` at the top, so may be broken by some options. Also AFAIK you cannot use this specific function without `setopt extendedglob` and `zmodload zsh/parameter` (for `$options`). // Do not forget to replace `~*.png` with whatever you need, I used this for testing purposes. >>>    Ciao >>> >>>    Dominik ^_^ ^_^ >>> >>>    -- >>> >>>    Dominik Vogt >>>    IBM Germany