|
|
|
|
||||||
| comp.unix.shell Using and programming the Unix shell. |
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
I'm curious how to write the double "triangle" loop for $* in posix sh
analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; preferably in posix shell. I'm experienced shell scripter but here, I'm stumped: for x in $*; do for y in ????; do body; done; done; (???? must become the slice of $* list from $1 to one before the element represented by $x) Is it possible at all ? bash-specific solutions are welcome, too, but posix sh are preferred. Yakov |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
On 2006-11-01, Yakov wrote:
> I'm curious how to write the double "triangle" loop for $* in posix sh > analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; > preferably in posix shell. Straightforward POSIX: k=0 N=666 while [ "$k" -lt "$N" ] do j=0 while [ "$j" -lt $k" ] do : whatever j=$(( $j + 1 )) done k=$(( $k + 1 )) done > I'm experienced shell scripter but here, > I'm stumped: for x in $*; do for y in ????; do body; done; done; > (???? must become the slice of $* list from $1 to one before > the element represented by $x) Since you are using $* (instead of "$@", which is probably what you really want) that is straightforward: z= for x in $* do z="$z $x" for y in $z do : body done done To use "$@" is more complicated in a POSIX shell, but straightforward in bash: unset z for x in $* do z[${#z[@]}]=$x for y in "${z[@]}" do : body done done > Is it possible at all ? bash-specific solutions are welcome, too, > but posix sh are preferred. -- Chris F.A. Johnson, author <http://cfaj.freeshell.org/shell> Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress) ===== My code in this post, if any, assumes the POSIX locale ===== and is released under the GNU General Public Licence |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
On 31 Oct 2006 16:11:39 -0800, Yakov
<iler.ml@gmail.com> wrote: > I'm curious how to write the double "triangle" loop for $* in posix sh > analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; > preferably in posix shell. I'm experienced shell scripter but here, > I'm stumped: for x in $*; do for y in ????; do body; done; done; > (???? must become the slice of $* list from $1 to one before > the element represented by $x) > > Is it possible at all ? bash-specific solutions are welcome, too, > but posix sh are preferred. > Bash version 2 or later, zsh, and I think ksh93 have the syntax for (( k=0; k<N; k++ )) Some systems have the "seq" command: for x in `seq 1 10` -- Baker's First Law of Federal Geometry: A block grant is a solid mass of money surrounded on all sides by governors. |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
2006-10-31, 16:11(-08), Yakov:
> I'm curious how to write the double "triangle" loop for $* in posix sh > analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; > preferably in posix shell. I'm experienced shell scripter but here, > I'm stumped: for x in $*; do for y in ????; do body; done; done; > (???? must become the slice of $* list from $1 to one before > the element represented by $x) > > Is it possible at all ? bash-specific solutions are welcome, too, > but posix sh are preferred. [...] for x in $* is not correct. the "*" variable contains the concatenation of the positional parameters with spaces. As you left it unquoted it is split against spaces (the ones used for concatenation and the one in the positional parameters themselves)tabs and newline characters, and filename generation is performed (? is expanded to the list of single character files in the current directory for instance). The syntax is for i do ...; done or for i in "$@"; do ...; done You can do: k=1 while [ "$k" -le "$#" ]; do eval "arg_k=\${$k}" j=1 while [ "$j" -lt "$k" ]; do eval "arg_j=\${$j}" # body j=$(($j + 1)) done k=$(($k + 1)) done Or: k=0 for arg_k do j=0 for arg_j do [ "$j" -ge "$k" ] && break # body j=$(($j + 1)) done k=$(($k + 1)) done -- Stéphane |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
2006-11-1, 00:22(-05), Bill Marcum:
> On 31 Oct 2006 16:11:39 -0800, Yakov > <iler.ml@gmail.com> wrote: >> I'm curious how to write the double "triangle" loop for $* in posix sh >> analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; >> preferably in posix shell. I'm experienced shell scripter but here, >> I'm stumped: for x in $*; do for y in ????; do body; done; done; >> (???? must become the slice of $* list from $1 to one before >> the element represented by $x) >> >> Is it possible at all ? bash-specific solutions are welcome, too, >> but posix sh are preferred. >> > Bash version 2 or later, zsh, and I think ksh93 have the syntax > for (( k=0; k<N; k++ )) I think that syntax appeared in 2.04, it's not in 2.03 which I beleive is still the version shipped with Solaris up to 9. > Some systems have the "seq" command: > for x in `seq 1 10` GNU systems, seq is a GNU only command. And that assumes the newline character is in IFS and that no digits are in IFS. x=1 while [ "$x" -le 10 ]; do ... x=$(($x + 1)) done is POSIX. zsh and bash3 (and possibly the latest version of ksh93) have: for x in {1..10}; do ...; done zsh has x=1; repeat 10 { ((x++)); ... } for x ({1..10}) {...} -- Stéphane |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
On 2006-11-01, Stephane CHAZELAS wrote:
> 2006-10-31, 16:11(-08), Yakov: >> I'm curious how to write the double "triangle" loop for $* in posix sh >> analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; >> preferably in posix shell. I'm experienced shell scripter but here, >> I'm stumped: for x in $*; do for y in ????; do body; done; done; >> (???? must become the slice of $* list from $1 to one before >> the element represented by $x) >> >> Is it possible at all ? bash-specific solutions are welcome, too, >> but posix sh are preferred. > [...] > > for x in $* > > is not correct. It may be correct. The OP may want it ... > split against spaces (the ones used for concatenation and > the one in the positional parameters themselves)tabs and newline > characters, and filename generation is performed (? is expanded > to the list of single character files in the current directory > for instance). .... -- Chris F.A. Johnson, author <http://cfaj.freeshell.org/shell> Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress) ===== My code in this post, if any, assumes the POSIX locale ===== and is released under the GNU General Public Licence |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
2006-11-1, 02:56(-05), Chris F.A. Johnson:
> On 2006-11-01, Stephane CHAZELAS wrote: >> 2006-10-31, 16:11(-08), Yakov: >>> I'm curious how to write the double "triangle" loop for $* in posix sh >>> analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; >>> preferably in posix shell. I'm experienced shell scripter but here, >>> I'm stumped: for x in $*; do for y in ????; do body; done; done; >>> (???? must become the slice of $* list from $1 to one before >>> the element represented by $x) >>> >>> Is it possible at all ? bash-specific solutions are welcome, too, >>> but posix sh are preferred. >> [...] >> >> for x in $* >> >> is not correct. > > It may be correct. The OP may want it ... [...] It is syntactically correct but does not make any sense. It only makes sense (with the default value of IFS and setting of -f) if the positional parameters are to be taken as blank separated lists of file patterns, as in $1 == "*.txt foo*", $2 = "*.bar", and even then it's quite dangerous given the way the shells treat patterns that don't have any match. With some other values of IFS, the behavior varies from shell to shell. With zsh (when not in sh or ksh mode), for x in $* loops over the non-empty positional parameters, a bit like in IFS=; set -f; for x in $* in some other shells. -- Stéphane |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
On 2006-11-01, Stephane CHAZELAS wrote:
> 2006-11-1, 02:56(-05), Chris F.A. Johnson: >> On 2006-11-01, Stephane CHAZELAS wrote: >>> 2006-10-31, 16:11(-08), Yakov: >>>> I'm curious how to write the double "triangle" loop for $* in posix sh >>>> analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; >>>> preferably in posix shell. I'm experienced shell scripter but here, >>>> I'm stumped: for x in $*; do for y in ????; do body; done; done; >>>> (???? must become the slice of $* list from $1 to one before >>>> the element represented by $x) >>>> >>>> Is it possible at all ? bash-specific solutions are welcome, too, >>>> but posix sh are preferred. >>> [...] >>> >>> for x in $* >>> >>> is not correct. >> >> It may be correct. The OP may want it ... > [...] > > It is syntactically correct but does not make any sense. Of course it mase sense. It may not give the desired behaviour in most cases, but it may well be what is wanted. > It only makes sense (with the default value of IFS and setting of > -f) if the positional parameters are to be taken as blank separated > lists of file patterns, as in $1 == "*.txt foo*", $2 = "*.bar", and > even then it's quite dangerous given the way the shells treat > patterns that don't have any match. The may be what is wanted, with or without set -f. > With some other values of IFS, the behavior varies from shell to > shell. > > With zsh (when not in sh or ksh mode), > > for x in $* > > loops over the non-empty positional parameters, a bit like in > > IFS=; set -f; for x in $* > > in some other shells. > -- Chris F.A. Johnson, author <http://cfaj.freeshell.org/shell> Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress) ===== My code in this post, if any, assumes the POSIX locale ===== and is released under the GNU General Public Licence |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
2006-10-31, 20:23(-05), Chris F.A. Johnson:
[...] > To use "$@" is more complicated in a POSIX shell, but > straightforward in bash: > > unset z > for x in $* ITYM for x in "$@" > do > z[${#z[@]}]=$x > for y in "${z[@]}" > do > : body > done In the OP's requirement z[...] should be after the for y loop. > done [...] It may reveal more legible in zsh: z=() for x do for y in "$z[@]"; do : body done z+=$x done Also: awk ' BEGIN { for (i = 1; i < ARGC; i++) { for (j = 1; j < i; j++) { # body ARGV[i] ARGV[j] } } exit }' "$@" may reveal more appropriate if the purpose is to do arithmetic or text manipulation operations over the elements. -- Stéphane |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
Chris F.A. Johnson wrote:
> On 2006-11-01, Yakov wrote: > > I'm curious how to write the double "triangle" loop for $* in posix sh > > analogous to this loop: for(k=0; k<N;k++) for(j=0; j<k; j++) body; > > preferably in posix shell. > > I'm experienced shell scripter but here, > > I'm stumped: for x in $*; do for y in ????; do body; done; done; > > (???? must become the slice of $* list from $1 to one before > > the element represented by $x) > > Since you are using $* (instead of "$@", which is probably what you > really want) that is straightforward: > > z= > for x in $* > do > z="$z $x" > for y in $z > do > : body > done > done This is nice, thanx, just what I was looking for. > To use "$@" is more complicated in a POSIX shell, but > straightforward in bash: > > unset z > for x in $* > do > z[${#z[@]}]=$x > for y in "${z[@]}" > do > : body > done > done Nice, too. Yakov |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
Stephane CHAZELAS wrote:
> 2006-11-1, 00:22(-05), Bill Marcum: > >>Some systems have the "seq" command: >>for x in `seq 1 10` > > GNU systems, seq is a GNU only command. And that assumes the > newline character is in IFS and that no digits are in IFS. If one fiddles with IFS he should take care that any changed IFS has just some well defined preferable local scope, take some precautions to save/restore it, or define it for a single command only. Janis |
|
|
|
#12 |
|
Messages: n/a
Hébergeur: |
Janis Papanagnou <Janis_Papanagnou@hotmail.com> writes:
>Stephane CHAZELAS wrote: >> 2006-11-1, 00:22(-05), Bill Marcum: >> >>>Some systems have the "seq" command: >>>for x in `seq 1 10` >> >> GNU systems, seq is a GNU only command. And that assumes the >> newline character is in IFS and that no digits are in IFS. >If one fiddles with IFS he should take care that any changed >IFS has just some well defined preferable local scope, take >some precautions to save/restore it, or define it for a single >command only. And who needs seq (for most purposes) when you have: `yes "" | cat -n | head -10` for 10 fill in any number. Casper |
|
|
|
#13 |
|
Messages: n/a
Hébergeur: |
On 2006-11-01, Casper H.S Dik wrote:
> Janis Papanagnou <Janis_Papanagnou@hotmail.com> writes: > >>Stephane CHAZELAS wrote: >>> 2006-11-1, 00:22(-05), Bill Marcum: >>> >>>>Some systems have the "seq" command: >>>>for x in `seq 1 10` >>> >>> GNU systems, seq is a GNU only command. And that assumes the >>> newline character is in IFS and that no digits are in IFS. > >>If one fiddles with IFS he should take care that any changed >>IFS has just some well defined preferable local scope, take >>some precautions to save/restore it, or define it for a single >>command only. > > > And who needs seq (for most purposes) when you have: > > `yes "" | cat -n | head -10` > > > for 10 fill in any number. But who says you have it? $ yes "" | cat -n | head -10 cat: cannot open -n: No such file or directory It works with most versions of cat, but it is not part of the standard. -- Chris F.A. Johnson, author <http://cfaj.freeshell.org/shell> Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress) ===== My code in this post, if any, assumes the POSIX locale ===== and is released under the GNU General Public Licence |
|
|
|
#14 |
|
Messages: n/a
Hébergeur: |
2006-11-01, 16:27(+01), Janis Papanagnou:
> Stephane CHAZELAS wrote: >> 2006-11-1, 00:22(-05), Bill Marcum: >> >>>Some systems have the "seq" command: >>>for x in `seq 1 10` >> >> GNU systems, seq is a GNU only command. And that assumes the >> newline character is in IFS and that no digits are in IFS. > > If one fiddles with IFS he should take care that any changed > IFS has just some well defined preferable local scope, take > some precautions to save/restore it, or define it for a single > command only. [...] That's one approach. An approach that makes more sense to me (in scripts) is to define IFS each time you ask the shell to split something for you. That's what you do in other languages. -- Stéphane |
|
|
|
#15 |
|
Messages: n/a
Hébergeur: |
2006-11-01, 15:58(+00), Casper H.S Dik:
> Janis Papanagnou <Janis_Papanagnou@hotmail.com> writes: > >>Stephane CHAZELAS wrote: >>> 2006-11-1, 00:22(-05), Bill Marcum: >>> >>>>Some systems have the "seq" command: >>>>for x in `seq 1 10` >>> >>> GNU systems, seq is a GNU only command. And that assumes the >>> newline character is in IFS and that no digits are in IFS. > >>If one fiddles with IFS he should take care that any changed >>IFS has just some well defined preferable local scope, take >>some precautions to save/restore it, or define it for a single >>command only. > > > And who needs seq (for most purposes) when you have: > > `yes "" | cat -n | head -10` > > > for 10 fill in any number. [...] Note that neither yes nor cat -n nor head -10 are SUSv3 though they are supported by most systems You can do: NL=' ' yes() { ( IFS=" " printf '%s\n' "$*" | sed -e :1 -e p -e b1 ) } cat_n() { nl -ba -d "$NL" } and do: yes "" | cat_n | head -n 10 or: for x in 1 2 3 4 5 6 7 8 9 10 -- Stéphane |
|
|
|
#16 |
|
Messages: n/a
Hébergeur: |
Le Wed, 01 Nov 2006 17:05:15 +0000, Stephane CHAZELAS a écrit:
> 2006-11-01, 15:58(+00), Casper H.S Dik: >> Janis Papanagnou <Janis_Papanagnou@hotmail.com> writes: >> >>>Stephane CHAZELAS wrote: >>>> 2006-11-1, 00:22(-05), Bill Marcum: >>>> >>>>>Some systems have the "seq" command: >>>>>for x in `seq 1 10` >>>> >>>> GNU systems, seq is a GNU only command. And that assumes the >>>> newline character is in IFS and that no digits are in IFS. >> >>>If one fiddles with IFS he should take care that any changed >>>IFS has just some well defined preferable local scope, take >>>some precautions to save/restore it, or define it for a single >>>command only. >> >> >> And who needs seq (for most purposes) when you have: >> >> `yes "" | cat -n | head -10` >> >> >> for 10 fill in any number. > [...] > > Note that neither yes nor cat -n nor head -10 are SUSv3 though > they are supported by most systems > > You can do: > > NL=' > ' > > yes() { > ( > IFS=" " > printf '%s\n' "$*" | > sed -e :1 -e p -e b1 > ) > } > > cat_n() { > nl -ba -d "$NL" > } > > and do: > > yes "" | cat_n | head -n 10 > > or: > > for x in 1 2 3 4 5 6 7 8 9 10 Then, while at replacing 'yes' and 'act -n' to simulate 'seq' why not directly simulate 'seq' ? here follows a possible form, I guess Chris F.A. Johnson and/or youreslf will correct the possible holes in it :-) $ cat seq_in_shell.sh #!/bin/bash ### ### ### In the "horrible tools without tools" collection. ### ### ### Admitting you miss the 'seq' utility and you don't ### have access to the source and/or a C compiler. ### Well, also admitting you have some spare time ### and you are kin to the March Hare here's a ### horrible still effective bash way to fill your dreams. ### ### ### _min=1 _max=$1 (( $# > 1 )) && _max=$2 && _min=$1 _count=$_min (( _max=${_max} + 1 )) (( _max - $_min )) || exit while true do printf ${_count}"\n" (( _count=${_count} + 1 )) (( ${_max} - ${_count} )) || exit done |
|
|
|
#17 |
|
Messages: n/a
Hébergeur: |
Chris F.A. Johnson wrote:
> On 2006-11-01, Casper H.S Dik wrote: >>Janis Papanagnou <Janis_Papanagnou@hotmail.com> writes: >>>Stephane CHAZELAS wrote: >>>>2006-11-1, 00:22(-05), Bill Marcum: >>>> >>>>>Some systems have the "seq" command: >>>>>for x in `seq 1 10` >>>> >>>>GNU systems, seq is a GNU only command. And that assumes the >>>>newline character is in IFS and that no digits are in IFS. >> >>>If one fiddles with IFS he should take care that any changed >>>IFS has just some well defined preferable local scope, take >>>some precautions to save/restore it, or define it for a single >>>command only. >> >>And who needs seq (for most purposes) when you have: >> >> `yes "" | cat -n | head -10` >> >>for 10 fill in any number. > > But who says you have it? > > $ yes "" | cat -n | head -10 > cat: cannot open -n: No such file or directory > > It works with most versions of cat, but it is not part of the > standard. $ yes "" | head -10 | awk '{print NR}' Janis |
|
|
|
#18 |
|
Messages: n/a
Hébergeur: |
2006-11-01, 18:41(+01), Loki Harfagr:
> Le Wed, 01 Nov 2006 17:05:15 +0000, Stephane CHAZELAS a écrit: > >> 2006-11-01, 15:58(+00), Casper H.S Dik: >>> Janis Papanagnou <Janis_Papanagnou@hotmail.com> writes: >>> >>>>Stephane CHAZELAS wrote: >>>>> 2006-11-1, 00:22(-05), Bill Marcum: >>>>> >>>>>>Some systems have the "seq" command: >>>>>>for x in `seq 1 10` >>>>> >>>>> GNU systems, seq is a GNU only command. And that assumes the >>>>> newline character is in IFS and that no digits are in IFS. >>> >>>>If one fiddles with IFS he should take care that any changed >>>>IFS has just some well defined preferable local scope, take >>>>some precautions to save/restore it, or define it for a single >>>>command only. >>> >>> >>> And who needs seq (for most purposes) when you have: >>> >>> `yes "" | cat -n | head -10` >>> >>> >>> for 10 fill in any number. >> [...] >> >> Note that neither yes nor cat -n nor head -10 are SUSv3 though >> they are supported by most systems >> >> You can do: >> >> NL=' >> ' >> >> yes() { >> ( >> IFS=" " >> printf '%s\n' "$*" | >> sed -e :1 -e p -e b1 >> ) >> } >> >> cat_n() { >> nl -ba -d "$NL" >> } >> >> and do: >> >> yes "" | cat_n | head -n 10 >> >> or: >> >> for x in 1 2 3 4 5 6 7 8 9 10 > > Then, while at replacing 'yes' and 'act -n' to simulate 'seq' > why not directly simulate 'seq' ? > here follows a possible form, I guess Chris F.A. Johnson and/or > youreslf will correct the possible holes in it :-) [...] #! /usr/bin/awk -f BEGIN { min=1 step = 1 max = ARGV[1] if (ARGC > 2) { min = max max = ARGV[2] } if (ARGC > 3) step = ARGV[3] if ((max - min) * step < 0) exit(1) for (i = min; i*step <= max*step; i+=step) printf "%.17g\n", i exit(0) } -- Stéphane |
|
|
|
#19 |
|
Messages: n/a
Hébergeur: |
2006-11-01, 19:45(+01), Janis Papanagnou:
[...] > $ yes "" | head -10 | awk '{print NR}' yes "" | sed -n '=;10q' -- Stéphane |
|
|
|
#20 |
|
Messages: n/a
Hébergeur: |
On 2006-11-01, Loki Harfagr wrote:
.... > Then, while at replacing 'yes' and 'act -n' to simulate 'seq' > why not directly simulate 'seq' ? > here follows a possible form, I guess Chris F.A. Johnson and/or > youreslf will correct the possible holes in it :-) > > $ cat seq_in_shell.sh > #!/bin/bash > ### > ### > ### In the "horrible tools without tools" collection. > ### > ### > ### Admitting you miss the 'seq' utility and you don't > ### have access to the source and/or a C compiler. > ### Well, also admitting you have some spare time > ### and you are kin to the March Hare here's a > ### horrible still effective bash way to fill your dreams. > ### > ### > ### > _min=1 > _max=$1 > (( $# > 1 )) && _max=$2 && _min=$1 Why use non-standard syntax? > _count=$_min > (( _max=${_max} + 1 )) > (( _max - $_min )) || exit > while true > do > printf ${_count}"\n" > (( _count=${_count} + 1 )) > (( ${_max} - ${_count} )) || exit > done seq() { inc=1 min=1 case $# in 1) max=$1 ;; 2) min=$1; max=$2; ;; 3) min=$1; inc=$2; max=$3 ;; *) echo "Too few arguments" >&2; return 1 ;; esac [ $inc -eq 0 ] && return 1 while : do [ $inc -gt 0 ] && [ $min -gt $max ] && break [ $inc -lt 0 ] && [ $min -lt $max ] && break printf "%d\n" "$min" min=$(( $min + $inc )) done } -- Chris F.A. Johnson, author <http://cfaj.freeshell.org/shell> Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress) ===== My code in this post, if any, assumes the POSIX locale ===== and is released under the GNU General Public Licence |
|
|
|
#21 |
|
Messages: n/a
Hébergeur: |
Le Wed, 01 Nov 2006 15:26:17 -0500, Chris F.A. Johnson a écrit:
.... >> (( $# > 1 )) && _max=$2 && _min=$1 > > Why use non-standard syntax? Well, I reckon it was to entice you to offer your fully standard version I'm enjoying your book (recipes) but haven't read it all over again ;-) (note that I was honest and "she-banged" '#!/bin/bash') |
|
![]() |
| Outils de la discussion | |
|
|