Posted: Fri May 16, 2008 4:10 am Post subject: How to do nothing when "for i in *" runs in empty directory?
Dear Experts,
I often write simple loops over the contents of the current directory
like this:
for i in *
do
.... do something with file $i ....
done
Typically, I actually want the loop to execute zero times when the
directory is empty; this isn't what happens, it runs once with $i ==
'*'. I know that this is the expected behaviour.
Can anyone suggest the simplest idiom to get the behaviour that I want?
The shortest code that I can think of is to put a test like this at
the start of the loop:
if [ $i == '*' ]
then
break
fi
(Hmm, I worry about what quoting I need to make that work.) But is
there something simpler that I can write, e.g. maybe something in place
of * in the for line?
Bash-isms are OK, though of course something portable is better.
Posted: Fri May 16, 2008 4:10 am Post subject: Re: How to do nothing when "for i in *" runs in empty direct
Phil wrote:
Quote:
Dear Experts,
I often write simple loops over the contents of the current directory
like this:
for i in *
do
.... do something with file $i ....
done
Typically, I actually want the loop to execute zero times when the
directory is empty; this isn't what happens, it runs once with $i ==
'*'. I know that this is the expected behaviour.
Can anyone suggest the simplest idiom to get the behaviour that I
want? The shortest code that I can think of is to put a test like
this at the start of the loop:
if [ $i == '*' ]
then
break
fi
(Hmm, I worry about what quoting I need to make that work.) But is
there something simpler that I can write, e.g. maybe something in
place of * in the for line?
for f in $(ls)
do
.... do something with file $i ....
done
Quote:
Bash-isms are OK, though of course something portable is better.
Should be portable, but woule exute another programe (ls) rather then the
Posted: Fri May 16, 2008 4:10 am Post subject: Re: How to do nothing when "for i in *" runs in empty direct
On Thursday 15 May 2008 15:38, Phil wrote:
Quote:
Can anyone suggest the simplest idiom to get the behaviour that I want?
The shortest code that I can think of is to put a test like this at
the start of the loop:
if [ $i == '*' ]
then
break
fi
(Hmm, I worry about what quoting I need to make that work.) But is
there something simpler that I can write, e.g. maybe something in place
of * in the for line?
Bash-isms are OK, though of course something portable is better.
(In what follows I assume you're not concerned about hidden files, since "*"
doesn't include them anyway)
shopt -s nullglob does what you want (you don't even need to modify your
existing code), although it's a bash-ism.
Methods that check whether "*" expands to "*" will fail when a single file whose
name is "*" exists...so maybe:
if [ "$(ls)" != "" ]; then
# directory is not empty
fi
(I'll be happy to learn other, possibly more robust, ways)
Note that in a multiuser, multiprocess system you can never be 100% sure that,
if a command tells you that a directory is empty, that directory will still be
empty when you start doing something on it (or viceversa, of course). If you're
sure you're the only user/process who's using the directory, then you don't
have to worry, of course.
Posted: Fri May 16, 2008 4:10 am Post subject: Re: How to do nothing when "for i in *" runs in empty direct
2008-05-15, 16:11(+02), Joachim Schmitz:
[...]
Quote:
(Hmm, I worry about what quoting I need to make that work.) But is
there something simpler that I can write, e.g. maybe something in
place of * in the for line?
for f in $(ls)
do
.... do something with file $i ....
done
If you're goind to do that, you should first disable filename
generation (set -f) and set the field separator to newline only
(IFS='
'), but then that means that it won't work anymore with
filenames with NL character.
Quote:
Bash-isms are OK, though of course something portable is better.
Should be portable, but woule exute another programe (ls) rather then the
shell doing it itself
The problem is that the output of ls is not post-processable
reliably unless you use non standard options like GNU's
--quoting-style.
Other ways:
set -- [*] *
case $1$2 in
("[*]*") ;;
(*)
shift
for i
do something with "$i"
done
;;
esac
If you're only interested in regular files (or symlinks to
regular files), you can do:
for i in *; do
[ -f "$i" ] || continue
something with "$i"
done
Non-standard:
zsh:
for i in *(N)
do something with "$i"
done
bash:
(
shopt -s nullglob
for i in *
do something with "$i"
done
)
Posted: Fri May 16, 2008 1:10 pm Post subject: Re: How to do nothing when "for i in *" runs in empty direct
In article <863aoj1rx8.fsf@localhost.localdomain>,
Maxwell Lol <nospam@com.invalid> wrote:
Quote:
Phil <spam_from_usenet08@chezphil.org> writes:
Dear Experts,
I often write simple loops over the contents of the current directory
like this:
for i in *
I use
for i in $*
$* is the parameters to the script, and has nothing to do with what's in
the current directory.
--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
To sum up, $* or $@ should never be used unquoted.
Well, depends if the script will *ever* see odd filenames or not. If
it's a throw-away script that will be used once, for a specific set of
files, it doesn't matter.
Posted: Fri May 16, 2008 11:10 pm Post subject: Re: How to do nothing when "for i in *" runs in empty direct
2008-05-15, 22:47(-04), Barry Margolin:
Quote:
In article <863aoj1rx8.fsf@localhost.localdomain>,
Maxwell Lol <nospam@com.invalid> wrote:
Phil <spam_from_usenet08@chezphil.org> writes:
Dear Experts,
I often write simple loops over the contents of the current directory
like this:
for i in *
I use
for i in $*
$* is the parameters to the script, and has nothing to do with what's in
the current directory.
[...]
It's even worth than that. $* is the *concatenation* of the
parameters with spaces for the Bourne shell or the first
character in IFS (or space if IFS is unset) for POSIX shells,
and as $* has been left unquoted, it is further split into words
according to $IFS (*) and filename generation is performed on
each resulting word, so you end up with a list of parameter that
is not necessarily the same as the parameters to the script.
The parameters to the script would have been "$@" (*with* the
quotes) in POSIX shells or ${1+"$@"} in the Bourne shell, but
then best is to use the more portable:
for i
alone.
To sum up, $* or $@ should never be used unquoted. "$*" should
be used for the concatenation of the parameters, and "$@" for
the list of parameters.
(*) It can be slightly different in some shells like bash and
some ksh implementation, where $* is split into the list of
parameters before being filename expanded even if IFS is empty.
Compare with the Bourne shell where if the parameters are a, b,
c and IFS is ":", $* expands to one single argument "a b c" as
you would expect. pdksh based shells show even more bogus
behavior.
To sum up, $* or $@ should never be used unquoted.
Well, depends if the script will *ever* see odd filenames or not. If
it's a throw-away script that will be used once, for a specific set of
files, it doesn't matter.
Here's a case where I actually agree with Stephane. Just get in the
habit of always using "$@", never $* and never unquoted. It doesn't
hurt anything, and it's the right thing to do.
Posted: Sat May 17, 2008 11:10 pm Post subject: Re: How to do nothing when "for i in *" runs in empty direct
On May 15, 10:31 am, Stephane Chazelas
<stephane.chaze...@unicyclist.com> wrote:
Quote:
2008-05-15, 16:11(+02), Joachim Schmitz:
[...]
(Hmm, I worry about what quoting I need to make that work.) But is
there something simpler that I can write, e.g. maybe something in
place of * in the for line?
for f in $(ls)
do
.... do something with file $i ....
done
If you're goind to do that, you should first disable filename
generation (set -f) and set the field separator to newline only
(IFS='
'), but then that means that it won't work anymore with
filenames with NL character.
Bash-isms are OK, though of course something portable is better.
Should be portable, but woule exute another programe (ls) rather then the
shell doing it itself
The problem is that the output of ls is not post-processable
reliably unless you use non standard options like GNU's
--quoting-style.
Other ways:
set -- [*] *
case $1$2 in
("[*]*") ;;
(*)
shift
for i
do something with "$i"
done
;;
esac
If you're only interested in regular files (or symlinks to
regular files), you can do:
for i in *; do
[ -f "$i" ] || continue
something with "$i"
done
And if you just want to skip . and .. but not anything else
for i in [^.][.]?*; do
something with "$i"
done
Quote:
Non-standard:
zsh:
for i in *(N)
do something with "$i"
done
bash:
(
shopt -s nullglob
for i in *
do something with "$i"
done
)
The subshell idea is kind of cool too, but you need to make sure that
"do something with $i" doesn't involve setting a variable you might
want to make use of outside of the loop.
(Hmm, I worry about what quoting I need to make that work.) But is
there something simpler that I can write, e.g. maybe something in
place of * in the for line?
for f in $(ls)
do
.... do something with file $i ....
done
If you're goind to do that, you should first disable filename
generation (set -f) and set the field separator to newline only
(IFS='
'), but then that means that it won't work anymore with
filenames with NL character.
Bash-isms are OK, though of course something portable is better.
Should be portable, but woule exute another programe (ls) rather then the
shell doing it itself
The problem is that the output of ls is not post-processable
reliably unless you use non standard options like GNU's
--quoting-style.
Other ways:
set -- [*] *
case $1$2 in
("[*]*") ;;
(*)
shift
for i
do something with "$i"
done
;;
esac
If you're only interested in regular files (or symlinks to
regular files), you can do:
for i in *; do
[ -f "$i" ] || continue
something with "$i"
done
And if you just want to skip . and .. but not anything else
for i in [^.][.]?*; do
Ooops. That should have been:
for i in [^.][^.]?*; do
# ^ -- not's can tie you in knots
[^.][^.] makes nice ASCII art too. Sort of like ^..^ with glasses
looking down and right (or left from the looker's perspective).
Quote:
something with "$i"
done
Non-standard:
zsh:
for i in *(N)
do something with "$i"
done
bash:
(
shopt -s nullglob
for i in *
do something with "$i"
done
)
The subshell idea is kind of cool too, but you need to make sure that
"do something with $i" doesn't involve setting a variable you might
want to make use of outside of the loop.
Posted: Sun May 18, 2008 11:10 pm Post subject: Re: How to do nothing when "for i in *" runs in empty direct
2008-05-17, 08:40(-07), r:
[...]
Quote:
for i in [^.][.]?*; do
Ooops. That should have been:
for i in [^.][^.]?*; do
# ^ -- not's can tie you in knots
[^.][^.] makes nice ASCII art too. Sort of like ^..^ with glasses
looking down and right (or left from the looker's perspective).
[...]
Could you please clarify what kind of filenames you're after
here? That looks like the more than 3 character files, where the
first 2 characters are not dots.
If you want all the files including hidden files but not "." and
"..", in zsh that's *(ND), and with other Bourne like-shells,
that would be: * .[!.]* ..?* for instance, and keeping in mind
that those patterns will expand to themselves if they don't
match?
Posted: Tue May 20, 2008 8:10 am Post subject: Re: How to do nothing when "for i in *" runs in empty direct
On 2008-05-19, r <rocky.bernstein@gmail.com> wrote:
Quote:
On May 18, 8:56 am, Stephane CHAZELAS <stephane_chaze...@yahoo.fr
wrote:
2008-05-17, 08:40(-07), r:
for i in [^.][^.]?*;do
Could you please clarify what kind of filenames you're after
here?
[...]
Quote:
This is a filesystem pattern match not a regexp. My understanding is
that [^.] matches anything which is not a dot (.).
That's one character and we don't want a zero length string anyway.
The next non-dot symbol is optional [^.]?
And finally * matches zero or more characters which along with the
first.
Now you're the one confusing regexps and filename globbing. [...]?
is _two_ patterns - a character range followed by any single
character. It is _not_ an optional character range.
Be wary of the the portability of inverted character ranges. I
_think_ it made it into POSIX but older Bourne shells and ksh88
may well lack it.
Posted: Tue May 20, 2008 8:10 am Post subject: Re: How to do nothing when "for i in *" runs in empty direct
On May 18, 8:56 am, Stephane CHAZELAS <stephane_chaze...@yahoo.fr>
wrote:
Quote:
2008-05-17, 08:40(-07), r:
[...]>> for i in [^.][.]?*;do
Ooops. That should have been:
for i in [^.][^.]?*;do
# ^ -- not's can tie you in knots
[^.][^.] makes nice ASCII art too. Sort of like ^..^ with glasses
looking down and right (or left from the looker's perspective).
[...]
Could you please clarify what kind of filenames you're after
here?
Anything that doesn't match . and .. and is not a null file which
generally you can't represent anyway.
Quote:
That looks like the more than 3 character files, where the
first 2 characters are not dots.
This is a filesystem pattern match not a regexp. My understanding is
that [^.] matches anything which is not a dot (.).
That's one character and we don't want a zero length string anyway.
The next non-dot symbol is optional [^.]?
And finally * matches zero or more characters which along with the
first.
Did I get this wrong?
Quote:
If you want all the files including hidden files but not "." and
"..", in zsh that's *(ND), and with other Bourne like-shells,
that would be: * .[!.]* ..?* for instance, and keeping in mind
that those patterns will expand to themselves if they don't
match?
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum