How do I write a script to count the total number of files, and directories in my home directory then display...
I want to do it with a for
loop. This is what I've come up with so far which does not work.
for home in /home/ {.,/}*; do echo "$home"; done
I would like to do this non-recursively if possible.
shell shell-script directory home
|
show 2 more comments
I want to do it with a for
loop. This is what I've come up with so far which does not work.
for home in /home/ {.,/}*; do echo "$home"; done
I would like to do this non-recursively if possible.
shell shell-script directory home
Interesting question. My first thought is that it's not possible, but I'm not sure. If it is possible it's a lot more difficult non-recursively. So much more difficult that for any tree structure traversal, recursion is the only way I can remember seeing it done.
– RobertL
Nov 10 '15 at 0:39
Well I suppose a recursive method would work too. What do you have in mind?
– David Prentice
Nov 10 '15 at 1:59
Since it changed, I'm editing your Title and Question a little.
– RobertL
Nov 10 '15 at 2:31
How aboutfind ~ | wc-l
?
– Jeff Schaller
Nov 10 '15 at 3:40
1
Please don't. Using the shell for this is much harder than it needs to be, very fragile and easy to get wrong, and orders of magnitude slower than usingfind
.
– terdon♦
Nov 10 '15 at 11:50
|
show 2 more comments
I want to do it with a for
loop. This is what I've come up with so far which does not work.
for home in /home/ {.,/}*; do echo "$home"; done
I would like to do this non-recursively if possible.
shell shell-script directory home
I want to do it with a for
loop. This is what I've come up with so far which does not work.
for home in /home/ {.,/}*; do echo "$home"; done
I would like to do this non-recursively if possible.
shell shell-script directory home
shell shell-script directory home
edited Feb 15 at 5:39
Rui F Ribeiro
40.7k1479137
40.7k1479137
asked Nov 10 '15 at 0:05
David PrenticeDavid Prentice
2491518
2491518
Interesting question. My first thought is that it's not possible, but I'm not sure. If it is possible it's a lot more difficult non-recursively. So much more difficult that for any tree structure traversal, recursion is the only way I can remember seeing it done.
– RobertL
Nov 10 '15 at 0:39
Well I suppose a recursive method would work too. What do you have in mind?
– David Prentice
Nov 10 '15 at 1:59
Since it changed, I'm editing your Title and Question a little.
– RobertL
Nov 10 '15 at 2:31
How aboutfind ~ | wc-l
?
– Jeff Schaller
Nov 10 '15 at 3:40
1
Please don't. Using the shell for this is much harder than it needs to be, very fragile and easy to get wrong, and orders of magnitude slower than usingfind
.
– terdon♦
Nov 10 '15 at 11:50
|
show 2 more comments
Interesting question. My first thought is that it's not possible, but I'm not sure. If it is possible it's a lot more difficult non-recursively. So much more difficult that for any tree structure traversal, recursion is the only way I can remember seeing it done.
– RobertL
Nov 10 '15 at 0:39
Well I suppose a recursive method would work too. What do you have in mind?
– David Prentice
Nov 10 '15 at 1:59
Since it changed, I'm editing your Title and Question a little.
– RobertL
Nov 10 '15 at 2:31
How aboutfind ~ | wc-l
?
– Jeff Schaller
Nov 10 '15 at 3:40
1
Please don't. Using the shell for this is much harder than it needs to be, very fragile and easy to get wrong, and orders of magnitude slower than usingfind
.
– terdon♦
Nov 10 '15 at 11:50
Interesting question. My first thought is that it's not possible, but I'm not sure. If it is possible it's a lot more difficult non-recursively. So much more difficult that for any tree structure traversal, recursion is the only way I can remember seeing it done.
– RobertL
Nov 10 '15 at 0:39
Interesting question. My first thought is that it's not possible, but I'm not sure. If it is possible it's a lot more difficult non-recursively. So much more difficult that for any tree structure traversal, recursion is the only way I can remember seeing it done.
– RobertL
Nov 10 '15 at 0:39
Well I suppose a recursive method would work too. What do you have in mind?
– David Prentice
Nov 10 '15 at 1:59
Well I suppose a recursive method would work too. What do you have in mind?
– David Prentice
Nov 10 '15 at 1:59
Since it changed, I'm editing your Title and Question a little.
– RobertL
Nov 10 '15 at 2:31
Since it changed, I'm editing your Title and Question a little.
– RobertL
Nov 10 '15 at 2:31
How about
find ~ | wc-l
?– Jeff Schaller
Nov 10 '15 at 3:40
How about
find ~ | wc-l
?– Jeff Schaller
Nov 10 '15 at 3:40
1
1
Please don't. Using the shell for this is much harder than it needs to be, very fragile and easy to get wrong, and orders of magnitude slower than using
find
.– terdon♦
Nov 10 '15 at 11:50
Please don't. Using the shell for this is much harder than it needs to be, very fragile and easy to get wrong, and orders of magnitude slower than using
find
.– terdon♦
Nov 10 '15 at 11:50
|
show 2 more comments
3 Answers
3
active
oldest
votes
There are many higher level commands that will almost do what you want to do, but this is a good demonstration of how to do the same thing in a shell script. This should work on any system that has /bin/sh
. It's not dependent on any other commands.
You can save this script as any filename, and then type sh ./whatever_you_named_it
to execute it.
I've split this into two sections, Listing the Files, and Counting the Files. Listing the files recursively is the most complicated, but once we have a list, it's easy to count them.
Listing the Files
This script recursively lists all of the files and directories under your home directory or a directory it receives as a parameter. With no parameters, it defaults to the home directory.
#!/bin/sh
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
for f in "$start"/* # step through all files in the starting directory
do
echo "$f" # print the file name
if test -d "$f" # is the file a directory? (-d)
then
sh "$0" "$f" # yes call this script with the dir as arg
fi
done
Let's discuss each step:
If no parameters were passed to the script, then use "$HOME" as the starting directory, otherwise use the first parameter. ($# contains the number of parameters passed to this script and we use the shell's built-in
test
command to find out if it's zero):
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
Iterate over files in your home directory.
"$HOME"/*
expands to all of the non-hidden files and directories at the top level of your home directory:
for f in "$HOME"/*
do
Print the file name. I think you know
echo
:
echo "$f" # print the file name
Call the shell built-in
test
command with the-d
option.test
returns true if the argument,"$f"
, is a directory. This is most often written as[ -d "$f" ]
for brevity, but it really is a command. [Search for "test expr" in thesh
man page]:
if test -d "$f" # is the file a directory? (-d)
then
This next statement is where we recur. We start a new copy of the currently running script and pass a the current "$f" directory name to it. The
$0
expands to the filename of the current shell script. Rather than type some filename here, we use$0
so this script will work even if it's name is changed. For example, if the script was calledrlist
, then you could also writesh rlist "$f"
, but if the script was renamed, the script would no longer function correctly, because it would be calling a non-existent script, or the wrong script. [See "Special Parameters" in thesh
man page]:
sh "$0" "$f" # yes call this script with the dir as arg
Terminates the
if
statement and thefor
loop.
fi
done
Counting the Files
The script so far lists the files but does not count them. If you want to get a count of all the files and directories execute this:
sh ./scriptname | wc -l
wc -l
is the "word count" command, and with the -l
option counts only the lines. The above pipeline prints a number which is the number of files under your home directory.
you shouldexec
at least, but it would be better to use a function. as is you need a shell process - and a separate PID - per child directory. it also branches into infinite loops fairly easily with a few nasty symlink placements - which would make it a fork bomb.
– mikeserv
Nov 10 '15 at 6:10
@mikeserv All true, but way beyond the level of understanding of the questioner.
– RobertL
Nov 10 '15 at 6:12
Let us continue this discussion in chat.
– RobertL
Nov 10 '15 at 6:17
@mikeserv Thanks! I can respond to that! Current version is tested. Thanks for bringing it down to my level! :--)
– RobertL
Nov 10 '15 at 19:39
@David-Prentice I just edited this to fix a serious problem. Please note the addition of theif
statement at the top of the script. This is critical to keep the script from looping for ever.
– RobertL
Nov 10 '15 at 19:41
|
show 4 more comments
with find
for not directory types in the current directory without recursing:
find . ! -name . -prune ! -type d | grep -c /
...for only directories drop the second bang, or for all filetypes drop the -type
test entirely.
that's easy in this case because without recursion we only ever see one path delimiter per file and so there is no confusion about what and where to count. counting newlines when you should be counting files can lead to trouble - the two things are unrelated. so what to do otherwise?
find .//. | grep -c '^.//.'
...will return an accurate count of child objects + this object rooted in the current directory.
equally as valid, but with reversed logic because it quotes newlines internally and probably faster because it needs only to stat()
each directory rather than every constituent file:
ls -1qRA . | grep -Exc [^/]+
if you drop the -R
option to ls
it will work without recursion just as well.
it is possible, though, that the above could return a false count depending on multibyte characters in filenames and incompatible locale settings. putting LC_ALL=C
in a POSIX-conformant ls
's environment would protect against that, and for depth counts of a sizable tree, doing so can only help matters with regards to performance after all.
add a comment |
Use find:
When you say non-recursively, did you mean that you only want a count of files/directories in /home, but not any subdirectories? If that is the case, you can restrict results to the top level with the maxdepth
option.
find /home -maxdepth 1 | wc -l
Using for:
i=0; for home in ~/*; do (( i++ )); done; echo $i
Note the spaces between the double parentheses and the enclosed i++.
Note that yourfind
breaks for file/dir names containing newlines and thefor
breaks for just about everything, including simple spaces. In any case, this is recursive and the OP asked for a non-recursive solution.
– terdon♦
Nov 10 '15 at 11:50
@terdon a bit harsh don't you think? The OP asked for a non-recursive solution andmaxdepth
would prevent recursion into subdirectories. The OP never mentioned filenames containing whitespace or newlines. Sure, that can be handled with quoting and-print0
, respectively, but I simply wasn't giving him more than he asked for.
– Nigel Tufnel
Nov 10 '15 at 14:37
Oh, good grief, sorry! I completely missed the-maxdepth
option. Could you at least edit it into all of your suggestions? Thefor
loop is still recursive. My main gripe though is i) this will break on strange filenames and ii) thefor item in $(find)
is very, very bad practice. At least use a shell globfor item in /home/*
There's no point in usingfind
if you specifically don't want recursion, that's just making it more complex and more fragile since it can't deal with whitespace correctly. The OP's original version deals with whitespace just fine.
– terdon♦
Nov 10 '15 at 14:43
are you jim morrison? @terdon - the home thing is not recursive. oh. the(find ~)
thing was in a previous version, yeah. thefor
loop is silly in any case:set -- ~/*; echo "$#"
would behave identically and without the fuss. it doesnt handle dotfiles though - except.
itself and only in the case of an empty directory. whats-maxdepth
all about, anyway? ive never understood what it does, except that its supposed to go in a special arg position. i mean, does it offer anything that a simple-prune
doesnt?
– mikeserv
Nov 10 '15 at 17:42
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f242002%2fhow-do-i-write-a-script-to-count-the-total-number-of-files-and-directories-in-m%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
There are many higher level commands that will almost do what you want to do, but this is a good demonstration of how to do the same thing in a shell script. This should work on any system that has /bin/sh
. It's not dependent on any other commands.
You can save this script as any filename, and then type sh ./whatever_you_named_it
to execute it.
I've split this into two sections, Listing the Files, and Counting the Files. Listing the files recursively is the most complicated, but once we have a list, it's easy to count them.
Listing the Files
This script recursively lists all of the files and directories under your home directory or a directory it receives as a parameter. With no parameters, it defaults to the home directory.
#!/bin/sh
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
for f in "$start"/* # step through all files in the starting directory
do
echo "$f" # print the file name
if test -d "$f" # is the file a directory? (-d)
then
sh "$0" "$f" # yes call this script with the dir as arg
fi
done
Let's discuss each step:
If no parameters were passed to the script, then use "$HOME" as the starting directory, otherwise use the first parameter. ($# contains the number of parameters passed to this script and we use the shell's built-in
test
command to find out if it's zero):
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
Iterate over files in your home directory.
"$HOME"/*
expands to all of the non-hidden files and directories at the top level of your home directory:
for f in "$HOME"/*
do
Print the file name. I think you know
echo
:
echo "$f" # print the file name
Call the shell built-in
test
command with the-d
option.test
returns true if the argument,"$f"
, is a directory. This is most often written as[ -d "$f" ]
for brevity, but it really is a command. [Search for "test expr" in thesh
man page]:
if test -d "$f" # is the file a directory? (-d)
then
This next statement is where we recur. We start a new copy of the currently running script and pass a the current "$f" directory name to it. The
$0
expands to the filename of the current shell script. Rather than type some filename here, we use$0
so this script will work even if it's name is changed. For example, if the script was calledrlist
, then you could also writesh rlist "$f"
, but if the script was renamed, the script would no longer function correctly, because it would be calling a non-existent script, or the wrong script. [See "Special Parameters" in thesh
man page]:
sh "$0" "$f" # yes call this script with the dir as arg
Terminates the
if
statement and thefor
loop.
fi
done
Counting the Files
The script so far lists the files but does not count them. If you want to get a count of all the files and directories execute this:
sh ./scriptname | wc -l
wc -l
is the "word count" command, and with the -l
option counts only the lines. The above pipeline prints a number which is the number of files under your home directory.
you shouldexec
at least, but it would be better to use a function. as is you need a shell process - and a separate PID - per child directory. it also branches into infinite loops fairly easily with a few nasty symlink placements - which would make it a fork bomb.
– mikeserv
Nov 10 '15 at 6:10
@mikeserv All true, but way beyond the level of understanding of the questioner.
– RobertL
Nov 10 '15 at 6:12
Let us continue this discussion in chat.
– RobertL
Nov 10 '15 at 6:17
@mikeserv Thanks! I can respond to that! Current version is tested. Thanks for bringing it down to my level! :--)
– RobertL
Nov 10 '15 at 19:39
@David-Prentice I just edited this to fix a serious problem. Please note the addition of theif
statement at the top of the script. This is critical to keep the script from looping for ever.
– RobertL
Nov 10 '15 at 19:41
|
show 4 more comments
There are many higher level commands that will almost do what you want to do, but this is a good demonstration of how to do the same thing in a shell script. This should work on any system that has /bin/sh
. It's not dependent on any other commands.
You can save this script as any filename, and then type sh ./whatever_you_named_it
to execute it.
I've split this into two sections, Listing the Files, and Counting the Files. Listing the files recursively is the most complicated, but once we have a list, it's easy to count them.
Listing the Files
This script recursively lists all of the files and directories under your home directory or a directory it receives as a parameter. With no parameters, it defaults to the home directory.
#!/bin/sh
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
for f in "$start"/* # step through all files in the starting directory
do
echo "$f" # print the file name
if test -d "$f" # is the file a directory? (-d)
then
sh "$0" "$f" # yes call this script with the dir as arg
fi
done
Let's discuss each step:
If no parameters were passed to the script, then use "$HOME" as the starting directory, otherwise use the first parameter. ($# contains the number of parameters passed to this script and we use the shell's built-in
test
command to find out if it's zero):
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
Iterate over files in your home directory.
"$HOME"/*
expands to all of the non-hidden files and directories at the top level of your home directory:
for f in "$HOME"/*
do
Print the file name. I think you know
echo
:
echo "$f" # print the file name
Call the shell built-in
test
command with the-d
option.test
returns true if the argument,"$f"
, is a directory. This is most often written as[ -d "$f" ]
for brevity, but it really is a command. [Search for "test expr" in thesh
man page]:
if test -d "$f" # is the file a directory? (-d)
then
This next statement is where we recur. We start a new copy of the currently running script and pass a the current "$f" directory name to it. The
$0
expands to the filename of the current shell script. Rather than type some filename here, we use$0
so this script will work even if it's name is changed. For example, if the script was calledrlist
, then you could also writesh rlist "$f"
, but if the script was renamed, the script would no longer function correctly, because it would be calling a non-existent script, or the wrong script. [See "Special Parameters" in thesh
man page]:
sh "$0" "$f" # yes call this script with the dir as arg
Terminates the
if
statement and thefor
loop.
fi
done
Counting the Files
The script so far lists the files but does not count them. If you want to get a count of all the files and directories execute this:
sh ./scriptname | wc -l
wc -l
is the "word count" command, and with the -l
option counts only the lines. The above pipeline prints a number which is the number of files under your home directory.
you shouldexec
at least, but it would be better to use a function. as is you need a shell process - and a separate PID - per child directory. it also branches into infinite loops fairly easily with a few nasty symlink placements - which would make it a fork bomb.
– mikeserv
Nov 10 '15 at 6:10
@mikeserv All true, but way beyond the level of understanding of the questioner.
– RobertL
Nov 10 '15 at 6:12
Let us continue this discussion in chat.
– RobertL
Nov 10 '15 at 6:17
@mikeserv Thanks! I can respond to that! Current version is tested. Thanks for bringing it down to my level! :--)
– RobertL
Nov 10 '15 at 19:39
@David-Prentice I just edited this to fix a serious problem. Please note the addition of theif
statement at the top of the script. This is critical to keep the script from looping for ever.
– RobertL
Nov 10 '15 at 19:41
|
show 4 more comments
There are many higher level commands that will almost do what you want to do, but this is a good demonstration of how to do the same thing in a shell script. This should work on any system that has /bin/sh
. It's not dependent on any other commands.
You can save this script as any filename, and then type sh ./whatever_you_named_it
to execute it.
I've split this into two sections, Listing the Files, and Counting the Files. Listing the files recursively is the most complicated, but once we have a list, it's easy to count them.
Listing the Files
This script recursively lists all of the files and directories under your home directory or a directory it receives as a parameter. With no parameters, it defaults to the home directory.
#!/bin/sh
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
for f in "$start"/* # step through all files in the starting directory
do
echo "$f" # print the file name
if test -d "$f" # is the file a directory? (-d)
then
sh "$0" "$f" # yes call this script with the dir as arg
fi
done
Let's discuss each step:
If no parameters were passed to the script, then use "$HOME" as the starting directory, otherwise use the first parameter. ($# contains the number of parameters passed to this script and we use the shell's built-in
test
command to find out if it's zero):
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
Iterate over files in your home directory.
"$HOME"/*
expands to all of the non-hidden files and directories at the top level of your home directory:
for f in "$HOME"/*
do
Print the file name. I think you know
echo
:
echo "$f" # print the file name
Call the shell built-in
test
command with the-d
option.test
returns true if the argument,"$f"
, is a directory. This is most often written as[ -d "$f" ]
for brevity, but it really is a command. [Search for "test expr" in thesh
man page]:
if test -d "$f" # is the file a directory? (-d)
then
This next statement is where we recur. We start a new copy of the currently running script and pass a the current "$f" directory name to it. The
$0
expands to the filename of the current shell script. Rather than type some filename here, we use$0
so this script will work even if it's name is changed. For example, if the script was calledrlist
, then you could also writesh rlist "$f"
, but if the script was renamed, the script would no longer function correctly, because it would be calling a non-existent script, or the wrong script. [See "Special Parameters" in thesh
man page]:
sh "$0" "$f" # yes call this script with the dir as arg
Terminates the
if
statement and thefor
loop.
fi
done
Counting the Files
The script so far lists the files but does not count them. If you want to get a count of all the files and directories execute this:
sh ./scriptname | wc -l
wc -l
is the "word count" command, and with the -l
option counts only the lines. The above pipeline prints a number which is the number of files under your home directory.
There are many higher level commands that will almost do what you want to do, but this is a good demonstration of how to do the same thing in a shell script. This should work on any system that has /bin/sh
. It's not dependent on any other commands.
You can save this script as any filename, and then type sh ./whatever_you_named_it
to execute it.
I've split this into two sections, Listing the Files, and Counting the Files. Listing the files recursively is the most complicated, but once we have a list, it's easy to count them.
Listing the Files
This script recursively lists all of the files and directories under your home directory or a directory it receives as a parameter. With no parameters, it defaults to the home directory.
#!/bin/sh
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
for f in "$start"/* # step through all files in the starting directory
do
echo "$f" # print the file name
if test -d "$f" # is the file a directory? (-d)
then
sh "$0" "$f" # yes call this script with the dir as arg
fi
done
Let's discuss each step:
If no parameters were passed to the script, then use "$HOME" as the starting directory, otherwise use the first parameter. ($# contains the number of parameters passed to this script and we use the shell's built-in
test
command to find out if it's zero):
if test $# -eq 0
then
startdir="$HOME"
else
startdir="$1"
fi
Iterate over files in your home directory.
"$HOME"/*
expands to all of the non-hidden files and directories at the top level of your home directory:
for f in "$HOME"/*
do
Print the file name. I think you know
echo
:
echo "$f" # print the file name
Call the shell built-in
test
command with the-d
option.test
returns true if the argument,"$f"
, is a directory. This is most often written as[ -d "$f" ]
for brevity, but it really is a command. [Search for "test expr" in thesh
man page]:
if test -d "$f" # is the file a directory? (-d)
then
This next statement is where we recur. We start a new copy of the currently running script and pass a the current "$f" directory name to it. The
$0
expands to the filename of the current shell script. Rather than type some filename here, we use$0
so this script will work even if it's name is changed. For example, if the script was calledrlist
, then you could also writesh rlist "$f"
, but if the script was renamed, the script would no longer function correctly, because it would be calling a non-existent script, or the wrong script. [See "Special Parameters" in thesh
man page]:
sh "$0" "$f" # yes call this script with the dir as arg
Terminates the
if
statement and thefor
loop.
fi
done
Counting the Files
The script so far lists the files but does not count them. If you want to get a count of all the files and directories execute this:
sh ./scriptname | wc -l
wc -l
is the "word count" command, and with the -l
option counts only the lines. The above pipeline prints a number which is the number of files under your home directory.
edited Nov 10 '15 at 19:36
answered Nov 10 '15 at 2:30
RobertLRobertL
4,867624
4,867624
you shouldexec
at least, but it would be better to use a function. as is you need a shell process - and a separate PID - per child directory. it also branches into infinite loops fairly easily with a few nasty symlink placements - which would make it a fork bomb.
– mikeserv
Nov 10 '15 at 6:10
@mikeserv All true, but way beyond the level of understanding of the questioner.
– RobertL
Nov 10 '15 at 6:12
Let us continue this discussion in chat.
– RobertL
Nov 10 '15 at 6:17
@mikeserv Thanks! I can respond to that! Current version is tested. Thanks for bringing it down to my level! :--)
– RobertL
Nov 10 '15 at 19:39
@David-Prentice I just edited this to fix a serious problem. Please note the addition of theif
statement at the top of the script. This is critical to keep the script from looping for ever.
– RobertL
Nov 10 '15 at 19:41
|
show 4 more comments
you shouldexec
at least, but it would be better to use a function. as is you need a shell process - and a separate PID - per child directory. it also branches into infinite loops fairly easily with a few nasty symlink placements - which would make it a fork bomb.
– mikeserv
Nov 10 '15 at 6:10
@mikeserv All true, but way beyond the level of understanding of the questioner.
– RobertL
Nov 10 '15 at 6:12
Let us continue this discussion in chat.
– RobertL
Nov 10 '15 at 6:17
@mikeserv Thanks! I can respond to that! Current version is tested. Thanks for bringing it down to my level! :--)
– RobertL
Nov 10 '15 at 19:39
@David-Prentice I just edited this to fix a serious problem. Please note the addition of theif
statement at the top of the script. This is critical to keep the script from looping for ever.
– RobertL
Nov 10 '15 at 19:41
you should
exec
at least, but it would be better to use a function. as is you need a shell process - and a separate PID - per child directory. it also branches into infinite loops fairly easily with a few nasty symlink placements - which would make it a fork bomb.– mikeserv
Nov 10 '15 at 6:10
you should
exec
at least, but it would be better to use a function. as is you need a shell process - and a separate PID - per child directory. it also branches into infinite loops fairly easily with a few nasty symlink placements - which would make it a fork bomb.– mikeserv
Nov 10 '15 at 6:10
@mikeserv All true, but way beyond the level of understanding of the questioner.
– RobertL
Nov 10 '15 at 6:12
@mikeserv All true, but way beyond the level of understanding of the questioner.
– RobertL
Nov 10 '15 at 6:12
Let us continue this discussion in chat.
– RobertL
Nov 10 '15 at 6:17
Let us continue this discussion in chat.
– RobertL
Nov 10 '15 at 6:17
@mikeserv Thanks! I can respond to that! Current version is tested. Thanks for bringing it down to my level! :--)
– RobertL
Nov 10 '15 at 19:39
@mikeserv Thanks! I can respond to that! Current version is tested. Thanks for bringing it down to my level! :--)
– RobertL
Nov 10 '15 at 19:39
@David-Prentice I just edited this to fix a serious problem. Please note the addition of the
if
statement at the top of the script. This is critical to keep the script from looping for ever.– RobertL
Nov 10 '15 at 19:41
@David-Prentice I just edited this to fix a serious problem. Please note the addition of the
if
statement at the top of the script. This is critical to keep the script from looping for ever.– RobertL
Nov 10 '15 at 19:41
|
show 4 more comments
with find
for not directory types in the current directory without recursing:
find . ! -name . -prune ! -type d | grep -c /
...for only directories drop the second bang, or for all filetypes drop the -type
test entirely.
that's easy in this case because without recursion we only ever see one path delimiter per file and so there is no confusion about what and where to count. counting newlines when you should be counting files can lead to trouble - the two things are unrelated. so what to do otherwise?
find .//. | grep -c '^.//.'
...will return an accurate count of child objects + this object rooted in the current directory.
equally as valid, but with reversed logic because it quotes newlines internally and probably faster because it needs only to stat()
each directory rather than every constituent file:
ls -1qRA . | grep -Exc [^/]+
if you drop the -R
option to ls
it will work without recursion just as well.
it is possible, though, that the above could return a false count depending on multibyte characters in filenames and incompatible locale settings. putting LC_ALL=C
in a POSIX-conformant ls
's environment would protect against that, and for depth counts of a sizable tree, doing so can only help matters with regards to performance after all.
add a comment |
with find
for not directory types in the current directory without recursing:
find . ! -name . -prune ! -type d | grep -c /
...for only directories drop the second bang, or for all filetypes drop the -type
test entirely.
that's easy in this case because without recursion we only ever see one path delimiter per file and so there is no confusion about what and where to count. counting newlines when you should be counting files can lead to trouble - the two things are unrelated. so what to do otherwise?
find .//. | grep -c '^.//.'
...will return an accurate count of child objects + this object rooted in the current directory.
equally as valid, but with reversed logic because it quotes newlines internally and probably faster because it needs only to stat()
each directory rather than every constituent file:
ls -1qRA . | grep -Exc [^/]+
if you drop the -R
option to ls
it will work without recursion just as well.
it is possible, though, that the above could return a false count depending on multibyte characters in filenames and incompatible locale settings. putting LC_ALL=C
in a POSIX-conformant ls
's environment would protect against that, and for depth counts of a sizable tree, doing so can only help matters with regards to performance after all.
add a comment |
with find
for not directory types in the current directory without recursing:
find . ! -name . -prune ! -type d | grep -c /
...for only directories drop the second bang, or for all filetypes drop the -type
test entirely.
that's easy in this case because without recursion we only ever see one path delimiter per file and so there is no confusion about what and where to count. counting newlines when you should be counting files can lead to trouble - the two things are unrelated. so what to do otherwise?
find .//. | grep -c '^.//.'
...will return an accurate count of child objects + this object rooted in the current directory.
equally as valid, but with reversed logic because it quotes newlines internally and probably faster because it needs only to stat()
each directory rather than every constituent file:
ls -1qRA . | grep -Exc [^/]+
if you drop the -R
option to ls
it will work without recursion just as well.
it is possible, though, that the above could return a false count depending on multibyte characters in filenames and incompatible locale settings. putting LC_ALL=C
in a POSIX-conformant ls
's environment would protect against that, and for depth counts of a sizable tree, doing so can only help matters with regards to performance after all.
with find
for not directory types in the current directory without recursing:
find . ! -name . -prune ! -type d | grep -c /
...for only directories drop the second bang, or for all filetypes drop the -type
test entirely.
that's easy in this case because without recursion we only ever see one path delimiter per file and so there is no confusion about what and where to count. counting newlines when you should be counting files can lead to trouble - the two things are unrelated. so what to do otherwise?
find .//. | grep -c '^.//.'
...will return an accurate count of child objects + this object rooted in the current directory.
equally as valid, but with reversed logic because it quotes newlines internally and probably faster because it needs only to stat()
each directory rather than every constituent file:
ls -1qRA . | grep -Exc [^/]+
if you drop the -R
option to ls
it will work without recursion just as well.
it is possible, though, that the above could return a false count depending on multibyte characters in filenames and incompatible locale settings. putting LC_ALL=C
in a POSIX-conformant ls
's environment would protect against that, and for depth counts of a sizable tree, doing so can only help matters with regards to performance after all.
edited Nov 10 '15 at 21:04
answered Nov 10 '15 at 5:17
mikeservmikeserv
45.8k668159
45.8k668159
add a comment |
add a comment |
Use find:
When you say non-recursively, did you mean that you only want a count of files/directories in /home, but not any subdirectories? If that is the case, you can restrict results to the top level with the maxdepth
option.
find /home -maxdepth 1 | wc -l
Using for:
i=0; for home in ~/*; do (( i++ )); done; echo $i
Note the spaces between the double parentheses and the enclosed i++.
Note that yourfind
breaks for file/dir names containing newlines and thefor
breaks for just about everything, including simple spaces. In any case, this is recursive and the OP asked for a non-recursive solution.
– terdon♦
Nov 10 '15 at 11:50
@terdon a bit harsh don't you think? The OP asked for a non-recursive solution andmaxdepth
would prevent recursion into subdirectories. The OP never mentioned filenames containing whitespace or newlines. Sure, that can be handled with quoting and-print0
, respectively, but I simply wasn't giving him more than he asked for.
– Nigel Tufnel
Nov 10 '15 at 14:37
Oh, good grief, sorry! I completely missed the-maxdepth
option. Could you at least edit it into all of your suggestions? Thefor
loop is still recursive. My main gripe though is i) this will break on strange filenames and ii) thefor item in $(find)
is very, very bad practice. At least use a shell globfor item in /home/*
There's no point in usingfind
if you specifically don't want recursion, that's just making it more complex and more fragile since it can't deal with whitespace correctly. The OP's original version deals with whitespace just fine.
– terdon♦
Nov 10 '15 at 14:43
are you jim morrison? @terdon - the home thing is not recursive. oh. the(find ~)
thing was in a previous version, yeah. thefor
loop is silly in any case:set -- ~/*; echo "$#"
would behave identically and without the fuss. it doesnt handle dotfiles though - except.
itself and only in the case of an empty directory. whats-maxdepth
all about, anyway? ive never understood what it does, except that its supposed to go in a special arg position. i mean, does it offer anything that a simple-prune
doesnt?
– mikeserv
Nov 10 '15 at 17:42
add a comment |
Use find:
When you say non-recursively, did you mean that you only want a count of files/directories in /home, but not any subdirectories? If that is the case, you can restrict results to the top level with the maxdepth
option.
find /home -maxdepth 1 | wc -l
Using for:
i=0; for home in ~/*; do (( i++ )); done; echo $i
Note the spaces between the double parentheses and the enclosed i++.
Note that yourfind
breaks for file/dir names containing newlines and thefor
breaks for just about everything, including simple spaces. In any case, this is recursive and the OP asked for a non-recursive solution.
– terdon♦
Nov 10 '15 at 11:50
@terdon a bit harsh don't you think? The OP asked for a non-recursive solution andmaxdepth
would prevent recursion into subdirectories. The OP never mentioned filenames containing whitespace or newlines. Sure, that can be handled with quoting and-print0
, respectively, but I simply wasn't giving him more than he asked for.
– Nigel Tufnel
Nov 10 '15 at 14:37
Oh, good grief, sorry! I completely missed the-maxdepth
option. Could you at least edit it into all of your suggestions? Thefor
loop is still recursive. My main gripe though is i) this will break on strange filenames and ii) thefor item in $(find)
is very, very bad practice. At least use a shell globfor item in /home/*
There's no point in usingfind
if you specifically don't want recursion, that's just making it more complex and more fragile since it can't deal with whitespace correctly. The OP's original version deals with whitespace just fine.
– terdon♦
Nov 10 '15 at 14:43
are you jim morrison? @terdon - the home thing is not recursive. oh. the(find ~)
thing was in a previous version, yeah. thefor
loop is silly in any case:set -- ~/*; echo "$#"
would behave identically and without the fuss. it doesnt handle dotfiles though - except.
itself and only in the case of an empty directory. whats-maxdepth
all about, anyway? ive never understood what it does, except that its supposed to go in a special arg position. i mean, does it offer anything that a simple-prune
doesnt?
– mikeserv
Nov 10 '15 at 17:42
add a comment |
Use find:
When you say non-recursively, did you mean that you only want a count of files/directories in /home, but not any subdirectories? If that is the case, you can restrict results to the top level with the maxdepth
option.
find /home -maxdepth 1 | wc -l
Using for:
i=0; for home in ~/*; do (( i++ )); done; echo $i
Note the spaces between the double parentheses and the enclosed i++.
Use find:
When you say non-recursively, did you mean that you only want a count of files/directories in /home, but not any subdirectories? If that is the case, you can restrict results to the top level with the maxdepth
option.
find /home -maxdepth 1 | wc -l
Using for:
i=0; for home in ~/*; do (( i++ )); done; echo $i
Note the spaces between the double parentheses and the enclosed i++.
edited Nov 10 '15 at 15:38
answered Nov 10 '15 at 4:26
Nigel TufnelNigel Tufnel
1394
1394
Note that yourfind
breaks for file/dir names containing newlines and thefor
breaks for just about everything, including simple spaces. In any case, this is recursive and the OP asked for a non-recursive solution.
– terdon♦
Nov 10 '15 at 11:50
@terdon a bit harsh don't you think? The OP asked for a non-recursive solution andmaxdepth
would prevent recursion into subdirectories. The OP never mentioned filenames containing whitespace or newlines. Sure, that can be handled with quoting and-print0
, respectively, but I simply wasn't giving him more than he asked for.
– Nigel Tufnel
Nov 10 '15 at 14:37
Oh, good grief, sorry! I completely missed the-maxdepth
option. Could you at least edit it into all of your suggestions? Thefor
loop is still recursive. My main gripe though is i) this will break on strange filenames and ii) thefor item in $(find)
is very, very bad practice. At least use a shell globfor item in /home/*
There's no point in usingfind
if you specifically don't want recursion, that's just making it more complex and more fragile since it can't deal with whitespace correctly. The OP's original version deals with whitespace just fine.
– terdon♦
Nov 10 '15 at 14:43
are you jim morrison? @terdon - the home thing is not recursive. oh. the(find ~)
thing was in a previous version, yeah. thefor
loop is silly in any case:set -- ~/*; echo "$#"
would behave identically and without the fuss. it doesnt handle dotfiles though - except.
itself and only in the case of an empty directory. whats-maxdepth
all about, anyway? ive never understood what it does, except that its supposed to go in a special arg position. i mean, does it offer anything that a simple-prune
doesnt?
– mikeserv
Nov 10 '15 at 17:42
add a comment |
Note that yourfind
breaks for file/dir names containing newlines and thefor
breaks for just about everything, including simple spaces. In any case, this is recursive and the OP asked for a non-recursive solution.
– terdon♦
Nov 10 '15 at 11:50
@terdon a bit harsh don't you think? The OP asked for a non-recursive solution andmaxdepth
would prevent recursion into subdirectories. The OP never mentioned filenames containing whitespace or newlines. Sure, that can be handled with quoting and-print0
, respectively, but I simply wasn't giving him more than he asked for.
– Nigel Tufnel
Nov 10 '15 at 14:37
Oh, good grief, sorry! I completely missed the-maxdepth
option. Could you at least edit it into all of your suggestions? Thefor
loop is still recursive. My main gripe though is i) this will break on strange filenames and ii) thefor item in $(find)
is very, very bad practice. At least use a shell globfor item in /home/*
There's no point in usingfind
if you specifically don't want recursion, that's just making it more complex and more fragile since it can't deal with whitespace correctly. The OP's original version deals with whitespace just fine.
– terdon♦
Nov 10 '15 at 14:43
are you jim morrison? @terdon - the home thing is not recursive. oh. the(find ~)
thing was in a previous version, yeah. thefor
loop is silly in any case:set -- ~/*; echo "$#"
would behave identically and without the fuss. it doesnt handle dotfiles though - except.
itself and only in the case of an empty directory. whats-maxdepth
all about, anyway? ive never understood what it does, except that its supposed to go in a special arg position. i mean, does it offer anything that a simple-prune
doesnt?
– mikeserv
Nov 10 '15 at 17:42
Note that your
find
breaks for file/dir names containing newlines and the for
breaks for just about everything, including simple spaces. In any case, this is recursive and the OP asked for a non-recursive solution.– terdon♦
Nov 10 '15 at 11:50
Note that your
find
breaks for file/dir names containing newlines and the for
breaks for just about everything, including simple spaces. In any case, this is recursive and the OP asked for a non-recursive solution.– terdon♦
Nov 10 '15 at 11:50
@terdon a bit harsh don't you think? The OP asked for a non-recursive solution and
maxdepth
would prevent recursion into subdirectories. The OP never mentioned filenames containing whitespace or newlines. Sure, that can be handled with quoting and -print0
, respectively, but I simply wasn't giving him more than he asked for.– Nigel Tufnel
Nov 10 '15 at 14:37
@terdon a bit harsh don't you think? The OP asked for a non-recursive solution and
maxdepth
would prevent recursion into subdirectories. The OP never mentioned filenames containing whitespace or newlines. Sure, that can be handled with quoting and -print0
, respectively, but I simply wasn't giving him more than he asked for.– Nigel Tufnel
Nov 10 '15 at 14:37
Oh, good grief, sorry! I completely missed the
-maxdepth
option. Could you at least edit it into all of your suggestions? The for
loop is still recursive. My main gripe though is i) this will break on strange filenames and ii) the for item in $(find)
is very, very bad practice. At least use a shell glob for item in /home/*
There's no point in using find
if you specifically don't want recursion, that's just making it more complex and more fragile since it can't deal with whitespace correctly. The OP's original version deals with whitespace just fine.– terdon♦
Nov 10 '15 at 14:43
Oh, good grief, sorry! I completely missed the
-maxdepth
option. Could you at least edit it into all of your suggestions? The for
loop is still recursive. My main gripe though is i) this will break on strange filenames and ii) the for item in $(find)
is very, very bad practice. At least use a shell glob for item in /home/*
There's no point in using find
if you specifically don't want recursion, that's just making it more complex and more fragile since it can't deal with whitespace correctly. The OP's original version deals with whitespace just fine.– terdon♦
Nov 10 '15 at 14:43
are you jim morrison? @terdon - the home thing is not recursive. oh. the
(find ~)
thing was in a previous version, yeah. the for
loop is silly in any case: set -- ~/*; echo "$#"
would behave identically and without the fuss. it doesnt handle dotfiles though - except .
itself and only in the case of an empty directory. whats -maxdepth
all about, anyway? ive never understood what it does, except that its supposed to go in a special arg position. i mean, does it offer anything that a simple -prune
doesnt?– mikeserv
Nov 10 '15 at 17:42
are you jim morrison? @terdon - the home thing is not recursive. oh. the
(find ~)
thing was in a previous version, yeah. the for
loop is silly in any case: set -- ~/*; echo "$#"
would behave identically and without the fuss. it doesnt handle dotfiles though - except .
itself and only in the case of an empty directory. whats -maxdepth
all about, anyway? ive never understood what it does, except that its supposed to go in a special arg position. i mean, does it offer anything that a simple -prune
doesnt?– mikeserv
Nov 10 '15 at 17:42
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f242002%2fhow-do-i-write-a-script-to-count-the-total-number-of-files-and-directories-in-m%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Interesting question. My first thought is that it's not possible, but I'm not sure. If it is possible it's a lot more difficult non-recursively. So much more difficult that for any tree structure traversal, recursion is the only way I can remember seeing it done.
– RobertL
Nov 10 '15 at 0:39
Well I suppose a recursive method would work too. What do you have in mind?
– David Prentice
Nov 10 '15 at 1:59
Since it changed, I'm editing your Title and Question a little.
– RobertL
Nov 10 '15 at 2:31
How about
find ~ | wc-l
?– Jeff Schaller
Nov 10 '15 at 3:40
1
Please don't. Using the shell for this is much harder than it needs to be, very fragile and easy to get wrong, and orders of magnitude slower than using
find
.– terdon♦
Nov 10 '15 at 11:50