|
|
|
|
||||||
| comp.unix.shell Using and programming the Unix shell. |
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
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" ------------------------------------------------------------------------ |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
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}' |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
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. |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
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++) |
|
|
|
#8 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#9 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#10 |
|
Messages: n/a
Hébergeur: |
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 |
|
|
|
#11 |
|
Messages: n/a
Hébergeur: |
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 |
|
![]() |
| Outils de la discussion | |
|
|