I want to keep all files not ending with
for f in $(ls | egrep -v .bat); do echo $f; done
for f in $(eval ls | egrep -v .bat); do echo $f; done
But both approaches yield the same result, as they print everything. Whereas
ls | egrep -v .bat and
eval ls | egrep -v .bat work per se, if used apart from the
It’s interesting to see that if I leave out the
-v flag, the loop does what it should and lists all files ending with
Feel free to edit the question title, as I was not sure what the problem is.
GNU bash, version 4.1.10(4)-release (i686-pc-cygwin).
$ ls -l | egrep -v ".bat" total 60K -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 fsc* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scala* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scalac* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scaladoc* -rwx------+ 1 SYSTEM SYSTEM 5.3K Jun 6 20:31 scalap*
Command is working, but not in the for loop.
$ for f in $(ls | egrep -v .bat); do echo $f; done fsc fsc.bat scala scala.bat scalac scalac.bat scaladoc scaladoc.bat scalap scalap.bat scalac scalac.bat scaladoc scaladoc.bat scalap scalap.bat
$ set -x
[email protected] /cygdrive/c/Program Files (x86)/scala/bin $ for f in $(ls | egrep -v .bat); do echo $f; done ++ ls -hF --color=tty ++ egrep --color=auto -v .bat + for f in '$(ls | egrep -v .bat)' + echo fsc fsc + for f in '$(ls | egrep -v .bat)' + echo fsc.bat fsc.bat // and so on
Here is Solutions:
We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.
A few things wrong in your code:
Using unquoted command substitution (
$(...)) without setting
Leaving expansions unquoted is the split+glob operator. The default is to split on space, tab and newline. Here, you only want to split on newline, so you need to set IFS to that as otherwise that means that will not work properly if filenames contain space or tab characters
Using unquoted command substitution without
Leaving expansions unquoted is the split+glob operator. Here you don’t want globbing, that is the expansion of wildcards such as
scala* into the list of matching files. When you do not want the shell to do globbing, you have to disable it with
ls aliased to
The issue above is aggravated by the fact that you have
ls aliased to
ls -F. Which adds
/ to directories and
* to executable files. So, typically, because
scala is executable,
ls -F outputs
scala*, and as a globbing pattern, it is expanded to all the filenames that start with
scala which explains why it seems like
egrep -v is not filtering files out.
Assuming filenames don’t contain newline characters
newline is as valid a character as any in a filename. So parsing the output of
ls typically doesn’t work. As for instance the output of
ls in a directory that contains
b files is the same as in a directory that contains one file called
egrep will filter the lines of the filenames, not the filenames
egrep instead of
egrep is deprecated.
grep -E is the standard equivalent.
Not escaping the
. regex operator.
Above, you used
egrep to enable extended regular expressions, but you don’t use any of the extended RE specific operator. The only RE operator you’re using is
. to match any character, while it looks like that’s not what you intended. So you might as well have used
grep -F here. Or use
grep -v '\.bat'.
Not anchoring the regexp on end-of-line
egrep .bat will match any line that contains any character followed by
bat, so that’s the regexp that means anything that contains
bat not in first position. It should have been
grep -v '\.bat$'.
Leaving an expansion unquoted is the split+glob operator. There, you want neither, so
$f should be quoted (
echo expands the ANSI C escape sequences in its arguments and/or treats strings like
-e specially depending on the
echo implementation (and/or the environment).
So a better solution:
for f in *; do case $f in (*.bat);; (*) printf '%s\n' "$f" esac done
Though if there’s no non-hidden file in the current directory, that will still output
*. You can work around that in
zsh by changing
*(N) or in
bash by running
shopt -s nullglob.
You should not use
ls to parse files this way.
First set extglob globstar by
shopt -s extglob globstar then
for f in !(*.bat) do printf '%s\n' "$f" done
find . -type f ! -name '*.bat'
Use the negation operator for a safer treat of files.
There’s nothing inherently wrong with your for loop:
$ for f in $(ls | egrep -v .bat); do echo $f; done
Here’s my output for the above command:
$ for f in $(ls | egrep -v .bat); do echo $f; done fsc scala scalac scaladoc scalap
One suggestion would be to quote the argument to
egrep, like so:
$ for f in $(ls | egrep -v '.bat'); do echo $f; done
Also take a look at the Bash Pitfalls wiki for other potential issues when scripting things in Bash such as this. In particular this sounds like the most plausible reason why you’re encountering this problem. Check to see if any of your files being returned contain spaces.
Another thing to try is to enable verbose debugging of your bash command, prior to running it so you can see what’s going on behind the scenes.
This enables debugging:
$ set -x
Then run your command:
$ for f in $(ls | egrep -v .bat); do echo $f; done ++ ls --color=auto ++ egrep --color=auto -v .bat + for f in '$(ls | egrep -v .bat)' + echo fsc fsc + for f in '$(ls | egrep -v .bat)' + echo scala scala + for f in '$(ls | egrep -v .bat)' + echo scalac scalac + for f in '$(ls | egrep -v .bat)' + echo scaladoc scaladoc + for f in '$(ls | egrep -v .bat)' + echo scalap scalap
Then disable it:
$ set +x
Issue with Cygwin?
Given the examples work fine on a number of native Linux systems the problem is most likely rooted in having something to do with Cygwin and/or it’s particular versions of
egrep. I’d pay particular attention to the field separator in Bash,
$IFS to see if there is an issue between the various line separators on Windows (
0x0d,0x0a) vs. Unix (
I do not have an installation of Cygwin so I have no method for proving this hypothesis.
Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂