0

I'd like to have standard parameters, i.e., my minimum reproducible example is:

#!/usr/bin/zsh a=${1:a} printf 'a: "%s"\n' "${a}" b=${2:./build} printf 'b: "%s"\n' "${b}" 

I'd expect to see, when running ./demo.zsh without arguments:

a: "a" b: "./build" 

Instead I get

a: "" ./demo.zsh:4: bad floating point constant 
  • why does ${1:a} seem to assume $1 to be set and non-zero, but the $a expanded is empty string?
  • where does the floating point misparsing come from? If I replace ./build with build, it complains about b being an unknown modifier.

If I run ./demo.zsh asdf bar, I get

a: "/tmp/fasf" ./demo.zsh:4: bad floating point constant 

which, frankly is even more confusing; where does /tmp/ come from (it's the cwd).

3
  • Did you mean to use ${1:-a} or ${1-a}? Is your question about what actually happens in the nonstandard expansion of ${1:a}? Commented Mar 10 at 11:37
  • @Kusalananda … yes, :-a. Time for another coffee. (an explanation of what happened there would still be welcome, but I don't want you to sink much time into that, I can look it up myself) Commented Mar 10 at 11:38
  • @Kusalananda that's just an offset specification, isn't it? Commented Mar 10 at 11:39

2 Answers 2

1

This builds on a comment from Stéphane.

In the Zsh shell, ${1:a} is the same as $1:a which is $1 with the :a modifier applied to it. The :a modifier is documented in zshexpn(1):

a Turn a file name into an absolute path: prepends the current directory, if necessary; remove `.' path segments; and remove `..' path segments and the segments that immediately precede them. 

Therefore, ${1:a} would be an empty string if $1 is empty or unset. It would be a pathname starting with /tmp/fasf if $1 is some non-empty string and your current directory was /tmp/fasf.

:. is not a valid modifier in the same sense as :a is, but the expansion ${1:n} would be valid for an integer n and would return the tail end of the string in $1, starting at the offset n. This is an inheritance from the ksh93 shell. This means that since . isn't a valid modifier, the shell tries to interpret it as an arithmetic expression for the purpose of computing a substring. This is why you get an error for ${2:./build}, the string ./build can't be used as an offset.

In the end, what you wanted to use was one of the standard ${1-a} or ${1:-a} expansion for providing a default value in the case $1 is unset or empty.

3
  • Note that like in ksh93, in ${var:offset} (which zsh eventually copied for compatibility with ksh even though it had $var[first,last]; and ${var:offset} conflicts with Bourne-style ${var:-default} and csh-style ${var:modifiers}), the offset can be an arithmetic expression (as long as it doesn't conflict with modifiers and like in ksh93 as long as it doesn't conflict with Bourne's ${var:-default}), so you can have ${1:.5/.1+3} for instance. The issue here is that . is not a valid floating point constant. .1/build would be OK. Commented Mar 10 at 16:36
  • ${var:offset} was introduced by ksh93 (late 1993), it wasn't in ksh88. ${var:-default} is from the Bourne shell (late 70s) and ${var:modifier} from csh (late 70s), though csh/tcsh didn't have that particular :a one (:a is for something else in tcsh) Commented Mar 10 at 17:10
  • @StéphaneChazelas Thanks, I misinterpreted your first comment. I have corrected the answer. Commented Mar 10 at 17:11
1

${1:a} This syntax is incorrect for providing default values. It tries to use a as a modifier, which is not what you intended.

The error messages bad floating point constant and unknown modifier are due to zsh trying to interpret a and ./build as modifiers or special syntax, which they are not.

I reproduced the script with Bash, and it should actually work the same way in Zsh.

#!/bin/bash a=${1:-a} printf 'a: "%s"\n' "${a}" b=${2:-./build} printf 'b: "%s"\n' "${b}" 

Output 1:

$:. ./test sss fff a: "sss" b: "fff" 

Output 2:

$:. ./test a: "a" b: "./build" 

The syntax ${1:a} is not the correct way to provide a default value for a positional parameter.

You should use ${1:-default} to provide a default value if the parameter is unset or nul.

${1:-a} This means if $1 is unset or null, use a as the default value.

${2:-./build} This means if $2 is unset or null, use ./build as the default value.

3
  • 1
    So, part of the issue is about what the "incorrect" ${1:a} is actually doing in zsh, because it obviously does something. The Bash shell has little to do with it. Commented Mar 10 at 11:45
  • 1
    thanks! Answers 1/3 of my questions (and as commented a bit before), so worth an upvote, but as Kusalanda says, what does happen when I use ${1:a} is a core question there. Could you elaborate? Commented Mar 10 at 11:48
  • 1
    $var:a gives an absolute path for the file stored in $var (see info zsh modifiers), while ${2:./build} as . is not a valid modifier in recent versions of zsh will interpret it as ${var:offset} à la ksh93. Commented Mar 10 at 11:54

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.