From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-1.0 required=5.0 tests=DKIM_ADSP_CUSTOM_MED, FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from primenet.com.au (ns1.primenet.com.au [203.24.36.2]) by inbox.vuxu.org (OpenSMTPD) with ESMTP id 324764a6 for ; Tue, 31 Dec 2019 09:17:07 +0000 (UTC) Received: (qmail 29874 invoked by alias); 31 Dec 2019 09:16:58 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: List-Unsubscribe: X-Seq: 45170 Received: (qmail 51 invoked by uid 1010); 31 Dec 2019 09:16:58 -0000 X-Qmail-Scanner-Diagnostics: from mail-wm1-f68.google.com by f.primenet.com.au (envelope-from , uid 7791) with qmail-scanner-2.11 (clamdscan: 0.102.1/25677. spamassassin: 3.4.2. Clear:RC:0(209.85.128.68):SA:0(-2.0/5.0):. Processed in 2.633583 secs); 31 Dec 2019 09:16:58 -0000 X-Envelope-From: stephane.chazelas@gmail.com X-Qmail-Scanner-Mime-Attachments: | X-Qmail-Scanner-Zip-Files: | Received-SPF: pass (ns1.primenet.com.au: SPF record at _netblocks.google.com designates 209.85.128.68 as permitted sender) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:subject:message-id:mail-followup-to :mime-version:content-disposition:user-agent; bh=fBgGX4bXrDQMxWmzUiTPdvS2sUfRqf5X7wlKXKiyQAk=; b=HnL2UcwhkIOTjgtr+qV4kw+MLGBG0XOYujoCoOJUwM3nwRmpbHJioHWzfu4hej+lQj QWCc4lkxyfqSbDx2Fau07plC6vOLDM+jDrAbs0gBfUGFnQvh9+ddbw7075CVf/kFFZus tRXEPBFu4D3+mGRiZtEflRfeeEne/Zo4ct6ZAVjgQHiiwfewB5rnPTzKZiZPhRkiqFTT u8yW8v7K2a4khiZxrGC8nm2tyV9Jj+d2xtVHKMnHg5/C+2kmzXkyV52ZZZxUedr2zpJ4 JczZ37PoJ+Ig1ENLjdtGzoJ0UOTCjkVg305M9ekEDp3uMser2OfsYfqd2mhbb8G0DabG cytA== X-Gm-Message-State: APjAAAUFW6L8EvVjCchghVzhELzY+lZlEDsp3H4/5o21uJQKIyWRwweF f+2qLJ6f6lV48+oFVm6X5FYR+64F X-Google-Smtp-Source: APXvYqz3K5bRUz/EuAWmfv2Uaxj1PWUB9CV9vyHdJXdpEzkg/AMgNh81WP7V3fdRS81NSYHQCbpoMQ== X-Received: by 2002:a05:600c:22d3:: with SMTP id 19mr3320956wmg.20.1577783780026; Tue, 31 Dec 2019 01:16:20 -0800 (PST) Date: Tue, 31 Dec 2019 09:16:18 +0000 From: Stephane Chazelas To: Zsh hackers list Subject: [PATCH] coding practice in examples (-- with globs mostly) Message-ID: <20191231091618.v2erkblkwkiy3i5a@chaz.gmail.com> Mail-Followup-To: Zsh hackers list MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: NeoMutt/20180716 Hello, I've spotted a few cases in the doc of things like "ls *" instead of "ls -d -- *". While that's something one could do interactively when they know how the files are named and their nature, that's bad practice in scripts. As a reference manual, IMO, the zsh documentation should show "the right way". - always use "--" when using globs or wherever the arguments are not known in advance and can't be guaranteed not to start with - or + - never use ls without -d unless you want it to list the *contents* of directory arguments. (I've not made a thorough scan, there might be more I missed). I include some related changes spotted on the way: - mention echo in the list of builtins that don't support -- - note print -C1 as the canonical way to print a list of arguments one per line as I've changed one example to use that. diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo index 415bce613..6c1c4f41a 100644 --- a/Doc/Zsh/builtins.yo +++ b/Doc/Zsh/builtins.yo @@ -39,11 +39,11 @@ be familiar to most command line users. Typically, options are single letters preceded by a hyphen (tt(-)). Options that take an argument accept it either immediately following the -option letter or after white space, for example `tt(print -C3 *)' or -`tt(print -C 3 *)' are equivalent. Arguments to options are not the +option letter or after white space, for example `tt(print -C3 {1..9})' or +`tt(print -C 3 {1..9})' are equivalent. Arguments to options are not the same as arguments to the command; the documentation indicates which is which. Options that do not take an argument may be combined in a single -word, for example `tt(print -ca *)' and `tt(print -c -a *)' are +word, for example `tt(print -rca -- *)' and `tt(print -r -c -a -- *)' are equivalent. Some shell builtin commands also take options that begin with `tt(+)' @@ -54,14 +54,14 @@ Options (together with their individual arguments, if any) must appear in a group before any non-option arguments; once the first non-option argument has been found, option processing is terminated. -All builtin commands other than precommand modifiers, even those that -have no options, can be given the argument `tt(-)tt(-)' to terminate option -processing. This indicates that the following words are non-option -arguments, but is otherwise ignored. This is useful in cases where -arguments to the command may begin with `tt(-)'. For historical -reasons, most builtin commands also recognize a single `tt(-)' in a -separate word for this purpose; note that this is less standard and -use of `tt(-)tt(-)' is recommended. +All builtin commands other than `tt(echo)' and precommand modifiers, +even those that have no options, can be given the argument `tt(-)tt(-)' +to terminate option processing. This indicates that the following words +are non-option arguments, but is otherwise ignored. This is useful in +cases where arguments to the command may begin with `tt(-)'. For +historical reasons, most builtin commands (including `tt(echo)') also +recognize a single `tt(-)' in a separate word for this purpose; note +that this is less standard and use of `tt(-)tt(-)' is recommended. startitem() prefix(-) @@ -114,9 +114,9 @@ var(text) is any non-empty string, it is replaced by the text a literal string, not a pattern. A trailing space in var(value) is not special in this case. For example, -example(alias -s ps=gv) +example(alias -s ps='gv --') -will cause the command `tt(*.ps)' to be expanded to `tt(gv *.ps)'. As +will cause the command `tt(*.ps)' to be expanded to `tt(gv -- *.ps)'. As alias expansion is carried out earlier than globbing, the `tt(*.ps)' will then be expanded. Suffix aliases constitute a different name space from other aliases (so in the above example it is still possible @@ -1258,7 +1258,11 @@ If given together with tt(-o) or tt(-O), sorting is performed case-independently. ) item(tt(-l))( -Print the arguments separated by newlines instead of spaces. +Print the arguments separated by newlines instead of spaces. Note: +if the list of arguments is empty, tt(print -l) as opposed to +tt(print -C1), still outputs one empty line; tt(print -rC1 -- +"$list[@]") is a more canonical way to print an arbitrary list of +arguments, one per line. ) item(tt(-m))( Take the first argument as a pattern (should be quoted), and remove @@ -1269,7 +1273,9 @@ item(tt(-n))( Do not add a newline to the output. ) item(tt(-N))( -Print the arguments separated and terminated by nulls. +Print the arguments separated and terminated by nulls. Again, +tt(print -rNC1 -- "$list[@]") is a canonical way to print an +arbitrary list as null-delimited records. ) item(tt(-o))( Print the arguments sorted in ascending order. diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo index 558342711..743ac73dc 100644 --- a/Doc/Zsh/contrib.yo +++ b/Doc/Zsh/contrib.yo @@ -4422,10 +4422,11 @@ For example, to get a long tt(ls) listing of all plain files in the current directory or its subdirectories: example(autoload -U zargs -zargs -- **/*(.) -- ls -l) +zargs -- **/*(.) -- ls -ld --) Note that `tt(-)tt(-)' is used both to mark the end of the var(option) -list and to mark the end of the var(input) list, so it must appear twice +list and to mark the end of the var(input) list (and here also to mark +the end of the option list for tt(ls)), so it must appear twice whenever the var(input) list may be empty. If there is guaranteed to be at least one var(input) and the first var(input) does not begin with a `tt(-)', then the first `tt(-)tt(-)' may be omitted. @@ -4435,7 +4436,7 @@ tt(-e) option may be used to change the end-of-inputs marker. Note that this does em(not) change the end-of-options marker. For example, to use `tt(..)' as the marker: -example(zargs -e.. -- **/*(.) .. ls -l) +example(zargs -e.. -- **/*(.) .. ls -ld --) This is a good choice in that example because no plain file can be named `tt(..)', but the best end-marker depends on the circumstances. diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo index d7147dbd7..6f4700dc4 100644 --- a/Doc/Zsh/expn.yo +++ b/Doc/Zsh/expn.yo @@ -400,7 +400,7 @@ backslashes. For example, the following piece of filename generation code with the tt(EXTENDED_GLOB) option: -example(print *.c+LPAR()#q:s/#%+LPAR()#b+RPAR()s+LPAR()*+RPAR().c/'S${match[1]}.C'/+RPAR()) +example(print -r -- *.c+LPAR()#q:s/#%+LPAR()#b+RPAR()s+LPAR()*+RPAR().c/'S${match[1]}.C'/+RPAR()) takes the expansion of tt(*.c) and applies the glob qualifiers in the tt(LPAR()#q)var(...)tt(RPAR()) expression, which consists of a substitution @@ -2492,11 +2492,11 @@ therefore matches files in the current directory as well as subdirectories. Thus: -example(ls (*/)#bar) +example(ls -ld -- (*/)#bar) or -example(ls **/bar) +example(ls -ld -- **/bar) does a recursive directory search for files named `tt(bar)' (potentially including the file `tt(bar)' in the current directory). This form does not @@ -2511,11 +2511,11 @@ they are treated as if both a tt(/) plus a further tt(*) are present. Hence: example(setopt GLOBSTARSHORT -ls **.c) +ls -ld -- **.c) is equivalent to -example(ls **/*.c) +example(ls -ld -- **/*.c) subsect(Glob Qualifiers) cindex(globbing, qualifiers) cindex(qualifiers, globbing) @@ -2707,7 +2707,7 @@ appropriate test. For example, example(nt+LPAR()RPAR() { [[ $REPLY -nt $NTREF ]] } NTREF=reffile -ls -l *(+nt)) +ls -ld -- *(+nt)) lists all files in the directory that have been modified more recently than tt(reffile). @@ -2898,36 +2898,36 @@ is performed, although note that the presence of the parentheses causes the entire expression to be subjected to any global pattern matching options such as tt(NULL_GLOB). Thus: -example(ls *(-/)) +example(ls -ld -- *(-/)) lists all directories and symbolic links that point to directories, and -example(ls *(-@)) +example(ls -ld -- *(-@)) lists all broken symbolic links, and -example(ls *(%W)) +example(ls -ld -- *(%W)) lists all world-writable device files in the current directory, and -example(ls *(W,X)) +example(ls -ld -- *(W,X)) lists all files in the current directory that are world-writable or world-executable, and -example(echo /tmp/foo*(u0^@:t)) +example(print -rC1 /tmp/foo*(u0^@:t)) outputs the basename of all root-owned files beginning with the string `tt(foo)' in tt(/tmp), ignoring symlinks, and -example(ls *.*~(lex|parse).[ch](^D^l1)) +example(ls -ld -- *.*~(lex|parse).[ch](^D^l1)) lists all files having a link count of one whose names contain a dot (but not those starting with a dot, since tt(GLOB_DOTS) is explicitly switched off) except for tt(lex.c), tt(lex.h), tt(parse.c) and tt(parse.h). -example(print b*.pro+LPAR()#q:s/pro/shmo/+RPAR()(#q.:s/builtin/shmiltin/)) +example(print -rC1 b*.pro+LPAR()#q:s/pro/shmo/+RPAR()(#q.:s/builtin/shmiltin/)) demonstrates how colon modifiers and other qualifiers may be chained together. The ordinary qualifier `tt(.)' is applied first, then the colon