f



parameter expansion

#!/bin/bash

tr -s' ' | cut -d' ' -f${*/%/,}

Example: ls -l | ./script 1 5
I would print the first and the 5th field of the input stream.

cut -d' ' -f1 -f5    -> cut -d' ' ${*/#/-f} don't run
cut -d' ' -f1, 5,     -> cut -d' ' ${*/%/,} don't run

I would expand -f1,5,7 etc.
How can I use parameters expansion in this script?

davide

[I'm sorry for my English...]


0
davide
2/6/2004 11:24:40 AM
comp.unix.shell 15430 articles. 1 followers. Post Follow

12 Replies
496 Views

Similar Articles

[PageSpeed] 23

On Fri, 06 Feb 2004 at 11:24 GMT, davide moro wrote:
> #!/bin/bash
> 
> tr -s' ' | cut -d' ' -f${*/%/,}
> 
> Example: ls -l | ./script 1 5
> I would print the first and the 5th field of the input stream.
> 
> cut -d' ' -f1 -f5    -> cut -d' ' ${*/#/-f} don't run
> cut -d' ' -f1, 5,     -> cut -d' ' ${*/%/,} don't run
> 
> I would expand -f1,5,7 etc.
> How can I use parameters expansion in this script?

#v+
#!/bin/bash

var="$@"
tr -s ' ' | cut -d ' ' -f "${var// /,}"
#v-

-- 
    Chris F.A. Johnson                        http://cfaj.freeshell.org
    ===================================================================
    My code (if any) in this post is copyright 2004, Chris F.A. Johnson
    and may be copied under the terms of the GNU General Public License
0
Chris
2/6/2004 1:50:01 PM
2004-02-06, 11:24(+00), davide moro:
> #!/bin/bash
>
> tr -s' ' | cut -d' ' -f${*/%/,}
>
> Example: ls -l | ./script 1 5
> I would print the first and the 5th field of the input stream.
>
> cut -d' ' -f1 -f5    -> cut -d' ' ${*/#/-f} don't run
> cut -d' ' -f1, 5,     -> cut -d' ' ${*/%/,} don't run
>
> I would expand -f1,5,7 etc.
> How can I use parameters expansion in this script?

#! /bin/sh -
IFS=,
tr -s ' ' | cut -d' ' -f"$*"

(not with Bourne shell nor ash, but with every POSIX compliant
shell [including bash] and zsh)

With zsh: ${(j",")@}

-- 
St�phane                      ["Stephane.Chazelas" at "free.fr"]
0
Stephane
2/6/2004 2:41:45 PM
Very kindly from you, I really appreciate your help.
Thanks!

Regards,

davide


0
davide
2/6/2004 2:58:36 PM
2004-02-6, 13:50(+00), Chris F.A. Johnson:
[...]
> #!/bin/bash
>
> var="$@"
> tr -s ' ' | cut -d ' ' -f "${var// /,}"
[...]

Note, to follow-up on my post on the other thread, that the fact
that in var="$@", the positional parameters are joined with
spaces is undocumented (in zsh, they are joined with the first
character of IFS, and it's documented)

Worse:
bash-2.05b$ set a b c; IFS=,; a=$*; b="$*"
bash-2.05b$ echo "$a"
a,b,c
bash-2.05b$ echo "$b"
a,b,c
bash-2.05b$ IFS=; a=$*; b="$*"
bash-2.05b$ echo "$a"
a b c
bash-2.05b$ echo "$b"
abc

So, in assignment, in bash, the only thing you can rely on in an
assignment WRT to $* and $@ is:

var="$*"

The other ones are not documented and may have erratic effects
that could change from a version of bash to another as it's not
documented (ksh93 behavior is different, and POSIX is not
clear on that point too, and if it makes things clearer, chances
are that the specification will get adapted so that ksh93 is
conformant):

var=$*
var=$@
var="$@"

Well, you can assume that as long as you don't change IFS, they
will always result in the positional parameters joined with
space.

-- 
St�phane                      ["Stephane.Chazelas" at "free.fr"]
0
Stephane
2/6/2004 3:43:26 PM
On Fri, 6 Feb 2004 16:43:26 +0100, Stephane CHAZELAS
<this.address@is.invalid> wrote:

>2004-02-6, 13:50(+00), Chris F.A. Johnson:
>[...]
>> #!/bin/bash
>>
>> var="$@"
>> tr -s ' ' | cut -d ' ' -f "${var// /,}"
>[...]
>
>Note, to follow-up on my post on the other thread, that the fact
>that in var="$@", the positional parameters are joined with
>spaces is undocumented (in zsh, they are joined with the first
>character of IFS, and it's documented)
>

No, you're failing again due to ignorance of bash info pages.

Under special parameters heading:

<quote>
`@'
     Expands to the positional parameters, starting from one.  When the
     expansion occurs within double quotes, each parameter expands to a
     separate word.  That is, `"$@"' is equivalent to `"$1" "$2" ...'.
     When there are no positional parameters, `"$@"' and `$@' expand to
     nothing (i.e., they are removed).

</quote>

>Worse:
>bash-2.05b$ set a b c; IFS=,; a=$*; b="$*"
>bash-2.05b$ echo "$a"
>a,b,c
>bash-2.05b$ echo "$b"
>a,b,c
>bash-2.05b$ IFS=; a=$*; b="$*"
>bash-2.05b$ echo "$a"
>a b c
>bash-2.05b$ echo "$b"
>abc
>

You assign null to IFS, and the behaviour conforms, well you guessed, to
related info page anyway:

<quote>
`*'
     Expands to the positional parameters, starting from one.  When the
     expansion occurs within double quotes, it expands to a single word
     with the value of each parameter separated by the first character
     of the `IFS' special variable.  That is, `"$*"' is equivalent to
     `"$1C$2C..."', where C is the first character of the value of the
     `IFS' variable.  If `IFS' is unset, the parameters are separated
     by spaces.  If `IFS' is null, the parameters are joined without
     intervening separators.

</quote>

I'm getting tired and bored of cut'n paste, are you not?


>So, in assignment, in bash, the only thing you can rely on in an
>assignment WRT to $* and $@ is:
>
>var="$*"
>
>The other ones are not documented and may have erratic effects
>that could change from a version of bash to another as it's not
>documented (ksh93 behavior is different, and POSIX is not
>clear on that point too, and if it makes things clearer, chances
>are that the specification will get adapted so that ksh93 is
>conformant):
>
>var=$*
>var=$@
>var="$@"
>
>Well, you can assume that as long as you don't change IFS, they
>will always result in the positional parameters joined with
>space.

0
A
2/6/2004 8:10:56 PM
2004-02-06, 22:10(+02), A Alper ATICI:
[...]
>>Note, to follow-up on my post on the other thread, that the fact
>>that in var="$@", the positional parameters are joined with
>>spaces is undocumented (in zsh, they are joined with the first
>>character of IFS, and it's documented)
>>
> No, you're failing again due to ignorance of bash info pages.
>
> Under special parameters heading:
>
> <quote>
> `@'
>      Expands to the positional parameters, starting from one.  When the
>      expansion occurs within double quotes, each parameter expands to a
>      separate word.  That is, `"$@"' is equivalent to `"$1" "$2" ...'.
>      When there are no positional parameters, `"$@"' and `$@' expand to
>      nothing (i.e., they are removed).
[...]

I can see no mention of "space" in the above. I can see that
"$@" expands to the list of positional parameters, but in the
context of an assignment to a string type variable, it makes no
sense (same in case statement, or [[ ]], of ${..#$@} and in many
other places). So, it has to be documented, and it's not, even
in the info page. That's simple enough, it's just a matter of �
in contexts where $@ can't expand to a list, then both $@ and
"$@" expand to the concatenation of positional arguments with
spaces inbetween, and $* expands to the concatenation of
positional parameters with the first char of IFS (or " " if IFS
is empty or unset) inbetween �.


>>bash-2.05b$ set a b c; IFS=,; a=$*; b="$*"
>>bash-2.05b$ echo "$a"
>>a,b,c
>>bash-2.05b$ echo "$b"
>>a,b,c
>>bash-2.05b$ IFS=; a=$*; b="$*"
>>bash-2.05b$ echo "$a"
>>a b c
>>bash-2.05b$ echo "$b"
>>abc
>>
>
> You assign null to IFS, and the behaviour conforms, well you guessed, to
> related info page anyway:
>
> <quote>
> `*'
>      Expands to the positional parameters, starting from one.  When the
>      expansion occurs within double quotes, it expands to a single word
>      with the value of each parameter separated by the first character
>      of the `IFS' special variable.  That is, `"$*"' is equivalent to
>      `"$1C$2C..."', where C is the first character of the value of the
>      `IFS' variable.  If `IFS' is unset, the parameters are separated
>      by spaces.  If `IFS' is null, the parameters are joined without
>      intervening separators.

Yes, and?

I was speaking of $*, $@, "$@" not documented in assignment in
that case. The part you quote speaks of "$*" for which I also
said that it was documented.

> </quote>
>
> I'm getting tired and bored of cut'n paste, are you not?

Could you please cut&paste a last one for me: the one that
explains why

in set a b c;IFS=",", a=$*, $a is "a,b,c"
while in set a b c;IFS="", a=$*, $a is "a b c"

(note that there are also serious bugs in UTF-8 locales with
non-ascii characters, but I guess UTF-8 support is still in a
very early alpha stage).

-- 
St�phane                      ["Stephane.Chazelas" at "free.fr"]
0
Stephane
2/6/2004 8:38:25 PM
On Fri, 6 Feb 2004 21:38:25 +0100, Stephane CHAZELAS
<this.address@is.invalid> wrote:
>>
>> Under special parameters heading:
>>
>> <quote>
>> `@'
>>      Expands to the positional parameters, starting from one.  When the
>>      expansion occurs within double quotes, each parameter expands to a
>>      separate word.  That is, `"$@"' is equivalent to `"$1" "$2" ...'.
>>      When there are no positional parameters, `"$@"' and `$@' expand to
>>      nothing (i.e., they are removed).
>[...]
>
>I can see no mention of "space" in the above. I can see that
>"$@" expands to the list of positional parameters, but in the
>context of an assignment to a string type variable, it makes no
>sense (same in case statement, or [[ ]], of ${..#$@} and in many
>other places). So, it has to be documented, and it's not, even
[...]

You seem to have a problem wrt "Expands to the positional parameters,
starting from one" statement, you may be right, and 
I'd say "have a look at arrays":

$ ary[0]=aa ary[1]=bb c=${ary[*]} d=${ary[@]}; echo $c; echo $d
aa bb
aa bb

If you agree an unseparated list is not an option in this case, then it
boils down to "why is it a space separated list?", but I regret I don't
have time to join that discussion.

Although I have not examined bash source code, it is obvious $* and $@ are
implemented as arrays, therefore their behaviour is not unreliable,
undocumented, erratic, etc to me. OTOH, I guess you've spotted a bash bug,
which is better obverved below:

$ set a b c; IFS=, a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
a b c
a,b,c
a b c
a,b,c

$ unset IFS
$ set a b c; a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
a b c
a b c
a b c
a b c

"$a" and "$b" exhibit "$*" specific behaviour as documented.
$a and $b expand to positional parameters starting from one, as in arrays.
So far so good.

$ set a b c; IFS= a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
a b c
a b c
abc
abc

Now, I'd expect 2nd and 3rd lines exchanged in the output above.
Unless there's a special case when IFS is null, this could be a bug, 
or I'm terribly wrong somewhere...

regards,


0
A
2/7/2004 2:24:39 PM
On Sat, 07 Feb 2004 at 14:24 GMT, A Alper ATICI wrote:
> 
> $ set a b c; IFS= a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
> a b c
> a b c
> abc
> abc
> 
> Now, I'd expect 2nd and 3rd lines exchanged in the output above.
> Unless there's a special case when IFS is null, this could be a bug, 
> or I'm terribly wrong somewhere...

   When $* is quoted, it's a single word; when it's not, each argument
   is a separate word, and $IFS is not used.

   You have assigned a single word, "abc" (the three arguments
   separated by the first character of $IFS, which is empty), to $b,
   but 3 words to $a.

   There's nothing wrong with the output; you might question why the
   three arguments were separated by spaces when assigned to
   $a. AFAICS, the separator is undefined. Once that's done, however,
   the output is entirely correct.

   The assignment to $b is well-defined, and once assigned, $b
   contains only a single word. There is no conceivable way that
   echoing "$b" or $b can produce anything but abc.

-- 
    Chris F.A. Johnson                        http://cfaj.freeshell.org
    ===================================================================
    My code (if any) in this post is copyright 2004, Chris F.A. Johnson
    and may be copied under the terms of the GNU General Public License
0
Chris
2/8/2004 5:56:48 PM
On 8 Feb 2004 17:56:48 GMT, "Chris F.A. Johnson" <c.fa.johnson@rogers.com>
wrote:

>On Sat, 07 Feb 2004 at 14:24 GMT, A Alper ATICI wrote:
>> 
>> $ set a b c; IFS= a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
>> a b c
>> a b c
>> abc
>> abc
>> 
>> Now, I'd expect 2nd and 3rd lines exchanged in the output above.
>> Unless there's a special case when IFS is null, this could be a bug, 
>> or I'm terribly wrong somewhere...
>
>   When $* is quoted, it's a single word; when it's not, each argument
>   is a separate word, and $IFS is not used.
>

This explanation conflicts with the case when IFS=,

$ set a b c; IFS=, a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
a b c
a,b,c
a b c
a,b,c

$* is not quoted when assigned to a, however IFS is used in echo "$a"



0
A
2/8/2004 7:02:01 PM
2004-02-8, 17:56(+00), Chris F.A. Johnson:
[...]
>    When $* is quoted, it's a single word; when it's not, each argument
>    is a separate word, and $IFS is not used.
[...]

Only in list contexts, but in scalar assignments and various
other place, you can't have "separate words" as it's a string
context.

And then, it is undocumented, that was my point.

Note that in

printf '<%s>\n' $*

in Bourne shells, the fact that printf gets passed the list of
arguments is because $* is splitted according to IFS as any
other variable. $* is not a special variable in Bourne shell
(except that its value is dynamically built as the concatenation
of the positional parameters with spaces inbetween)

In Bourne shells:

set "a:b" "A B"
IFS=:
printf '<%s>\n' $*

gives:

<a>
<b A B>

(because $* is joined with spaces, and thus not splitted there)

In Bourne shell, $@ is exactly the same as $* except when inside
double quotes in list context in which case it is expanded to
the list of positional parameters:

$ printf '<%s>\n' $@
<a>
<b A B>
$ printf '<%s>\n' "$@"
<a:b>
<A B>

That's very simple and consistent. The zsh way is very similar
(when word splitting is activated).

In bash, you don't really know how it works. In cmd $*, it seems
that you always get the list of parameters (word splitted). If
you disable word splitting (IFS=), the arguments are preserved,
even the empty ones:

$ set "a b" "" "c d"
$ IFS=
$ printf '<%s>\n' $*
<a b>
<>
<c d>

That would indicate that the separation is not due to word
splitting. However, in

LANG=fr_FR.UTF-8 bash -c "
  set a b
  IFS=\$'\303\251' # � in UTF8
  printf %s, \$* | od -tc"
0000000   a 303   b   ,
0000003

There's a general confusion, in bash multibyte support between
"character" and "byte". Here it seems that the value of $* is
built as the positional parameters joined with the first *byte*
of IFS, and so is not word splitted because the result is a
string that doesn't contain the '�' *character*.

So, it seems that at least in some cases, you have a behavior
similar as the Bourne shell one.

>    You have assigned a single word, "abc" (the three arguments
>    separated by the first character of $IFS, which is empty), to $b,
>    but 3 words to $a.

You can't assign 3 words to a string-type variable, that makes
no sense. What you can do is assigning the concatenation of 3
words with whatever character inbetween.

-- 
St�phane                      ["Stephane.Chazelas" at "free.fr"]
0
Stephane
2/8/2004 7:46:38 PM
On Sun, 08 Feb 2004 at 19:02 GMT, A Alper ATICI wrote:
> On 8 Feb 2004 17:56:48 GMT, "Chris F.A. Johnson" <c.fa.johnson@rogers.com>
> wrote:
> 
>>On Sat, 07 Feb 2004 at 14:24 GMT, A Alper ATICI wrote:
>>> 
>>> $ set a b c; IFS= a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
>>> a b c
>>> a b c
>>> abc
>>> abc
>>> 
>>> Now, I'd expect 2nd and 3rd lines exchanged in the output above.
>>> Unless there's a special case when IFS is null, this could be a bug, 
>>> or I'm terribly wrong somewhere...
>>
>>   When $* is quoted, it's a single word; when it's not, each argument
>>   is a separate word, and $IFS is not used.
>>
> 
> This explanation conflicts with the case when IFS=,
> 
> $ set a b c; IFS=, a=$* b="$*"; echo $a; echo "$a"; echo $b; echo "$b"
> a b c
> a,b,c
> a b c
> a,b,c
> 
> $* is not quoted when assigned to a, however IFS is used in echo "$a"

   No, IFS is used in the assignment to $a. As I said, when $* is not
   quoted, the separator is undefined. Apparently, it uses a space
   when IFS="", otherwise it uses the first character of $IFS.

   The output is exactly as it should be, given the values assigned
   to $a and $b.

man bash:

       echo [-neE] [arg ...]
              Output the args, separated by spaces, followed by a
              newline.


   When IFS=",", $a and $b are both read as three arguments (they both
   contain "a,b,c"), "$a" and "$b" are read as one argument.

$ set a b c; IFS=, a=$* b="$*"; IFS=; echo $a; echo "$a"; echo $b; echo "$b"
a,b,c
a,b,c
a,b,c
a,b,c


-- 
    Chris F.A. Johnson                        http://cfaj.freeshell.org
    ===================================================================
    My code (if any) in this post is copyright 2004, Chris F.A. Johnson
    and may be copied under the terms of the GNU General Public License
0
Chris
2/8/2004 7:57:33 PM
On 8 Feb 2004 19:57:33 GMT, "Chris F.A. Johnson" <c.fa.johnson@rogers.com>
wrote:

[...]
>
>       echo [-neE] [arg ...]
>              Output the args, separated by spaces, followed by a
>              newline.
>
>
>   When IFS=",", $a and $b are both read as three arguments (they both
>   contain "a,b,c"), "$a" and "$b" are read as one argument.
>
>$ set a b c; IFS=, a=$* b="$*"; IFS=; echo $a; echo "$a"; echo $b; echo "$b"
>a,b,c
>a,b,c
>a,b,c
>a,b,c


Aaaargghhh !  Word-splitting... 
It was always there, while I totally overlooked it (allegedly new semantics
of IFS within special parameters fooled me into oblivion).
All right, thank you very much.


0
A
2/8/2004 11:08:03 PM
Reply: