PHWinfo banniere

Titres
PORTAIL ANNUAIRE ARTICLES COMPARATEUR HÉBERGEURS DEVIS FORUMS RÉDUCTEUR D'URL
Précédent   PHWinfo > Forums Hébergement > Forum Serveur - Sécurité et techniques > comp.unix.shell > In bash, recursion breaks loops!?
S'inscrire FAQ Membres Recherche Messages du jour Marquer les forums comme lus
comp.unix.shell Using and programming the Unix shell.

In bash, recursion breaks loops!?

Réponse
 
LinkBack Outils de la discussion
Vieux 06/09/2007, 09h39   #1
SpecialTux86
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut In bash, recursion breaks loops!?

Greetings.

I have written a bash script which should receive an absolute directory path
(in parameter $1) and should return the maximum directory level found in
the whole subtree starting from $1.

For example, if I have

/a
/a/b
/a/c
/a/b/x
/a/b/y
/a/b/z
/a/b/z/j
/a/c/k

it should print 3 (the 3 subdirs in /a/b).

The problem is that the "echo" in the recursive function seems to show, in
my version of bash (3.1.17(1)) that recursion breaks the loop!

Please, could anyone me fix this code? (if possible, I'd like to keep
the recursive function)

Thanks, St86.


-------------------------------------------------------------------

#!/bin/bash

usage() {
echo
echo "Usage: $0 <dir>"
echo
exit 1
}


verifyLevel() {
if [ "$1" -gt "$maxLevel" ]
then
maxLevel="$1"
fi
}


processDir(){
cd "$1"
echo "In $1"

local dir
local counter=0

for dir in *
do
if [ -d "$dir" ]
then
counter=$(expr "$counter" + 1)
fi
done

verifyLevel "$counter"


for dir in *
do
if [ -d "$dir" ]
then
processDir "$1/$dir"
fi
done
}



if [ "$#" -ne 1 ]
then
usage
fi

dirName="$1"
maxLevel=0

if [ ! -d "$dirName" ] || [ ${dirName:0:1} != "/" ]
then
echo "Invalid directory!"
usage
fi


processDir "$dirName"

echo "$maxLevel"

------------------------------------------------------------------------
  Réponse avec citation
Vieux 06/09/2007, 15h16   #2
Ed Morton
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

SpecialTux86 wrote:
> Greetings.
>
> I have written a bash script which should receive an absolute directory path
> (in parameter $1) and should return the maximum directory level found in
> the whole subtree starting from $1.
>
> For example, if I have
>
> /a
> /a/b
> /a/c
> /a/b/x
> /a/b/y
> /a/b/z
> /a/b/z/j
> /a/c/k
>
> it should print 3 (the 3 subdirs in /a/b).
>
> The problem is that the "echo" in the recursive function seems to show, in
> my version of bash (3.1.17(1)) that recursion breaks the loop!


In what way? You should describe the symptoms you're seeing so it's
easier for us to you. In any case, rather than doing this recursive
cd-ing stuff, you should just use "find a -type d" and post-process the
output, e.g.:

find . -type d -print | awk -F/
'{a[NF]++;max=(a[NF]>m?a[NF]:m)}END{print m}'

Regards,

Ed.
  Réponse avec citation
Vieux 06/09/2007, 15h18   #3
Ed Morton
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

Ed Morton wrote:

> SpecialTux86 wrote:
>
>> Greetings.
>>
>> I have written a bash script which should receive an absolute

directory path
>> (in parameter $1) and should return the maximum directory level found in
>> the whole subtree starting from $1.
>> <snip>

>
> In what way? You should describe the symptoms you're seeing so it's

easier for us to you. In any case, rather than doing this recursive
cd-ing stuff, you should just use "find a -type d" and post-process the
output, e.g.:
>
> find . -type d -print | awk -F/

'{a[NF]++;max=(a[NF]>m?a[NF]:m)}END{print m}'

use max or m consistently:

find . -type d -print | awk -F/ '{a[NF]++;m=(a[NF]>m?a[NF]:m)}END{print m}'
  Réponse avec citation
Vieux 06/09/2007, 15h21   #4
Ed Morton
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

Ed Morton wrote:

> Ed Morton wrote:
>
> > SpecialTux86 wrote:
> >
> >> Greetings.
> >>
> >> I have written a bash script which should receive an absolute

> directory path
> >> (in parameter $1) and should return the maximum directory level

> found in
> >> the whole subtree starting from $1.
> >> <snip>

> >
> > In what way? You should describe the symptoms you're seeing so it's

> easier for us to you. In any case, rather than doing this recursive
> cd-ing stuff, you should just use "find a -type d" and post-process the
> output, e.g.:
> >
> > find . -type d -print | awk -F/

> '{a[NF]++;max=(a[NF]>m?a[NF]:m)}END{print m}'
>
> use max or m consistently:
>
> find . -type d -print | awk -F/ '{a[NF]++;m=(a[NF]>m?a[NF]:m)}END{print m}'


And, in case your script is really intended to count the max level as
you say in the text rather than what it currently does which is to count
the max number of subdirectores, you'd do this:

find . -type d -print | awk -F/ '{m=(NF>m?NF:m)}END{print m}'

Ed.
  Réponse avec citation
Vieux 06/09/2007, 17h32   #5
SpecialTux86
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

Ed Morton wrote:

> Ed Morton wrote:
>
>> Ed Morton wrote:
>>
>> > SpecialTux86 wrote:
>> >
>> >> Greetings.
>> >>
>> >> I have written a bash script which should receive an absolute

>> directory path
>> >> (in parameter $1) and should return the maximum directory level

>> found in
>> >> the whole subtree starting from $1.
>> >> <snip>
>> >
>> > In what way? You should describe the symptoms you're seeing so it's

>> easier for us to you. In any case, rather than doing this recursive
>> cd-ing stuff, you should just use "find a -type d" and post-process the
>> output, e.g.:
>> >
>> > find . -type d -print | awk -F/

>> '{a[NF]++;max=(a[NF]>m?a[NF]:m)}END{print m}'
>>
>> use max or m consistently:
>>
>> find . -type d -print | awk -F/ '{a[NF]++;m=(a[NF]>m?a[NF]:m)}END{print
>> m}'

>


First of all, thank you a lot for the very complex, professional solutions
you suggested.

However, as you can see reading my script, I must keep scripts as simple as
I can: this script was an exercise, which I tried to solve; I don't know
how to use awk (which is, as far as I know, a quite complex language, and I
can use just bash and its related, simple, tools).

The problem is that recursive function are, for me, very easy to read, and
fast to codify.


Now, please try to reproduce the problem as follows:

1)Create a directory structure like the one I specified in the previous
message (for example: "mkdir /opt/a", "mkdir /opt/a/b", ....)

2)Execute the script, by passing to it the absolute path of the "a"
directory (for example: "script.sh /opt/a"): you won't see all the
directories!
For example, I just get:

In /opt/a
In /opt/a/b
In /opt/a/b/x
3


3)Put the recursive call between (): this time, you'll see all the dir
names, but, because of the subshell, the script will be wrong.
In particular, I get:

In /opt/a
In /opt/a/b
In /opt/a/b/x
In /opt/a/b/y
In /opt/a/b/z
In /opt/a/b/z/j
In /opt/a/c
In /opt/a/c/k
2


(of course, since it considers just the /a directory)

Please, what should I do, in your opinion, to keep the recursive function?
What is this behaviour caused by? I have searched a lot in the bash
documents, but I found nothing about this...


Bye, St86
  Réponse avec citation
Vieux 06/09/2007, 17h39   #6
Ed Morton
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

SpecialTux86 wrote:
> Ed Morton wrote:
>
>
>>Ed Morton wrote:
>>
>>
>>>Ed Morton wrote:
>>>
>>> > SpecialTux86 wrote:
>>> >
>>> >> Greetings.
>>> >>
>>> >> I have written a bash script which should receive an absolute
>>>directory path
>>> >> (in parameter $1) and should return the maximum directory level
>>>found in
>>> >> the whole subtree starting from $1.
>>> >> <snip>
>>> >
>>> > In what way? You should describe the symptoms you're seeing so it's
>>>easier for us to you. In any case, rather than doing this recursive
>>>cd-ing stuff, you should just use "find a -type d" and post-process the
>>>output, e.g.:
>>> >
>>> > find . -type d -print | awk -F/
>>>'{a[NF]++;max=(a[NF]>m?a[NF]:m)}END{print m}'
>>>
>>>use max or m consistently:
>>>
>>>find . -type d -print | awk -F/ '{a[NF]++;m=(a[NF]>m?a[NF]:m)}END{print
>>>m}'

>>

>
> First of all, thank you a lot for the very complex, professional solutions
> you suggested.


You're welcome, but the above is a simple one-liner compared to what
you're trying to do with your fairly lengthy script.

> However, as you can see reading my script, I must keep scripts as simple as
> I can: this script was an exercise, which I tried to solve; I don't know
> how to use awk (which is, as far as I know, a quite complex language, and I
> can use just bash and its related, simple, tools).


There's nothing complex about awk. It's a simple, algol-derived (like
Pascal, C, etc.) language that follows a condition-action semantics
inside an implicit "while read field1 field2... fieldNF" loop and it's
primarily used for text processing.

> The problem is that recursive function are, for me, very easy to read, and
> fast to codify.


OK, but not every problem is best solved by a recursive algorithm. This
is one of them.

> Now, please try to reproduce the problem as follows:

<snip>
> Please, what should I do, in your opinion, to keep the recursive function?
> What is this behaviour caused by? I have searched a lot in the bash
> documents, but I found nothing about this...


Since this is the wrong approach to this problem, I'll leave it to
others to debug if they like.

Ed.
  Réponse avec citation
Vieux 06/09/2007, 19h23   #7
pgas
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

On Sep 6, 7:32 pm, SpecialTux86 <SpecialTu...@nospam.com> wrote:
> However, as you can see reading my script, I must keep scripts as simple as
> I can: this script was an exercise, which I tried to solve; I don't know
> how to use awk (which is, as far as I know, a quite complex language, and I
> can use just bash and its related, simple, tools).


awk is really simple, with less pitfalls than bash

> 1)Create a directory structure like the one I specified in the previous
> message (for example: "mkdir /opt/a", "mkdir /opt/a/b", ....)
>
> 2)Execute the script, by passing to it the absolute path of the "a"
> directory (for example: "script.sh /opt/a"): you won't see all the
> directories!
> For example, I just get:
>
> In /opt/a
> In /opt/a/b
> In /opt/a/b/x
> 3


because your processdir function doesn't leave the current directory
alone.
so in your for loop, the second call is done in the wrong directory.
(one deeper, at least)
just put cd .. before returning.
Or perhaps save the $PWD before cd $1 and do a cd to the old PWD
before leaving

>
> 3)Put the recursive call between (): this time, you'll see all the dir
> names, but, because of the subshell, the script will be wrong.


Because a subshell cannot modify variables of the parent shell.

PS:
try counter=$((counter)) or ((counter++)

  Réponse avec citation
Vieux 06/09/2007, 20h30   #8
Scott McMillan
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

On Thu, 06 Sep 2007 08:39:35 GMT, SpecialTux86
<SpecialTux86@nospam.com> wrote:

>Greetings.
>
>I have written a bash script which should receive an absolute directory path
>(in parameter $1) and should return the maximum directory level found in
>the whole subtree starting from $1.
>
>For example, if I have
>
>/a
>/a/b
>/a/c
>/a/b/x
>/a/b/y
>/a/b/z
>/a/b/z/j
>/a/c/k
>
>it should print 3 (the 3 subdirs in /a/b).

<snip>

Should it count hidden directories (those whose name begins with ".")
as well? If not, how about

ls -l $1 | grep "^d" | wc -l


if so, how about (maxdepth option may not be portable)

find $1 -type d -maxdepth 1 | wc -l


Scott McMillan
  Réponse avec citation
Vieux 06/09/2007, 20h30   #9
Chris F.A. Johnson
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

On 2007-09-06, SpecialTux86 wrote:
> Greetings.
>
> I have written a bash script which should receive an absolute directory path
> (in parameter $1) and should return the maximum directory level found in
> the whole subtree starting from $1.


As Ed pointed out, there are probably betters ways of doing this.
Sometimes a recursive script _is_ the better solution, so I have
added comments to your script.

> For example, if I have
>
> /a
> /a/b
> /a/c
> /a/b/x
> /a/b/y
> /a/b/z
> /a/b/z/j
> /a/c/k
>
> it should print 3 (the 3 subdirs in /a/b).
>
> The problem is that the "echo" in the recursive function seems to show, in
> my version of bash (3.1.17(1)) that recursion breaks the loop!


The problem is that you are not returning to the previous directory
after processing a directory.

> Please, could anyone me fix this code? (if possible, I'd like to keep
> the recursive function)
>
> -------------------------------------------------------------------
>
> #!/bin/bash
>
> usage() {
> echo
> echo "Usage: $0 <dir>"
> echo
> exit 1
> }
>
>
> verifyLevel() {
> if [ "$1" -gt "$maxLevel" ]
> then
> maxLevel="$1"
> fi
> }
>
>
> processDir(){


Add:

local olddir=$PWD

> cd "$1"
> echo "In $1"
>
> local dir
> local counter=0
>
> for dir in *
> do
> if [ -d "$dir" ]
> then
> counter=$(expr "$counter" + 1)


Don't use expr for integer arithmetic in a POSIX shell:

counter=$(( $counter + 1 ))

> fi
> done
>
> verifyLevel "$counter"
>
> for dir in *


You can get just the directories with:

for dir in */

> do
> if [ -d "$dir" ]
> then
> processDir "$1/$dir"
> fi
> done


Add:

cd "$olddir"

> }
>
>
>
> if [ "$#" -ne 1 ]
> then
> usage
> fi
>
> dirName="$1"
> maxLevel=0
>
> if [ ! -d "$dirName" ] || [ ${dirName:0:1} != "/" ]
> then
> echo "Invalid directory!"
> usage
> fi
>
>
> processDir "$dirName"
>
> echo "$maxLevel"
>
> ------------------------------------------------------------------------



--
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
  Réponse avec citation
Vieux 06/09/2007, 20h30   #10
SpecialTux86
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

pgas wrote:

> On Sep 6, 7:32 pm, SpecialTux86 <SpecialTu...@nospam.com> wrote:
>> However, as you can see reading my script, I must keep scripts as simple
>> as I can: this script was an exercise, which I tried to solve; I don't
>> know how to use awk (which is, as far as I know, a quite complex
>> language, and I can use just bash and its related, simple, tools).

>
> awk is really simple, with less pitfalls than bash
>


Yes, but I'm a student, and in the exam I *MUST* use just bash.
In real life, I prefer other languages, in particular Python, which is easy,
incredibly powerful and fast to codify.


>> 1)Create a directory structure like the one I specified in the previous
>> message (for example: "mkdir /opt/a", "mkdir /opt/a/b", ....)
>>
>> 2)Execute the script, by passing to it the absolute path of the "a"
>> directory (for example: "script.sh /opt/a"): you won't see all the
>> directories!
>> For example, I just get:
>>
>> In /opt/a
>> In /opt/a/b
>> In /opt/a/b/x
>> 3

>
> because your processdir function doesn't leave the current directory
> alone.
> so in your for loop, the second call is done in the wrong directory.
> (one deeper, at least)
> just put cd .. before returning.


YES!!!! ^______^ ^_______^ ^_______^ ^_______^ ^_______^

As you can see, I'm quite happy... that's because in the exam many exercises
require just such a recursive practice... and since - I repeat - I can just
use bash (and few simple commands) - having a script demonstrating me what
was wrong and how to fix it will me a lot in preparing the exam.

Recursion is not so elegant, or not so fast, in Bash, but if you just have
little time, and can use just few expressions, then it's useful.

> Or perhaps save the $PWD before cd $1 and do a cd to the old PWD
> before leaving


It is possible... but it seems that cd .. is 100% equal, and faster (in the
exam, I'll have very little time!)

>> 3)Put the recursive call between (): this time, you'll see all the dir
>> names, but, because of the subshell, the script will be wrong.

>
> Because a subshell cannot modify variables of the parent shell.
>
> PS:
> try counter=$((counter)) or ((counter++)


I think in the first one you have missed "+ 1"... anyway, I didn't know the
second, C-style, more elegant expression. Thanks.


Thank you a lot! ^_______^
St86
  Réponse avec citation
Vieux 06/09/2007, 21h32   #11
SpecialTux86
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: In bash, recursion breaks loops!?

Chris F.A. Johnson wrote:

> On 2007-09-06, SpecialTux86 wrote:
>
> As Ed pointed out, there are probably betters ways of doing this.
> Sometimes a recursive script _is_ the better solution, so I have
> added comments to your script.
>


Yes, in particular if one can use just Bash expressions, without more useful
commands...


>> The problem is that the "echo" in the recursive function seems to show,
>> in my version of bash (3.1.17(1)) that recursion breaks the loop!

>
> The problem is that you are not returning to the previous directory
> after processing a directory.
>


Yes. I'm not used to bash development (and to the "pwd" concept), so I
wasn't able even to see that bad mistake.


> Add:
>
> local olddir=$PWD
>


Ok. But also "cd .." in the end should work...

>
> Don't use expr for integer arithmetic in a POSIX shell:
>
> counter=$(( $counter + 1 ))


OK.

>
> You can get just the directories with:
>
> for dir in */
>



I have never found such a *VERY* quick pattern. Thank you!


Thank you a lot for your valuable advice. St86

  Réponse avec citation
Réponse


Outils de la discussion

Règles de messages
Vous ne pouvez pas créer de nouvelles discussions
Vous ne pouvez pas envoyer des réponses
Vous ne pouvez pas envoyer des pièces jointes
Vous ne pouvez pas modifier vos messages

Les balises BB sont activées : oui
Les smileys sont activés : oui
La balise [IMG] est activée : oui
Le code HTML peut être employé : non
Trackbacks are oui
Pingbacks are oui
Refbacks are oui


Fuseau horaire GMT +1. Il est actuellement 13h26.


Édité par : vBulletin® version 3.7.3
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.2.0 RC5 Tous droits réservés.
Version française #16 par l'association vBulletin francophone
PHWinfo est un site Éducation Sans Frontières ©2000-2008
Ad Management by RedTyger
©Tous droits réservés par les parties respectives
Page generated in 0,23186 seconds with 19 queries