我想保留所有文件不是结尾为.bat
我试过
for f in $(ls | egrep -v .bat); do echo $f; done
和
for f in $(eval ls | egrep -v .bat); do echo $f; done
但这两种方法都会产生相同的结果,因为它们会打印所有内容。而如果脱离循环使用, ls | egrep -v .bat
andeval ls | egrep -v .bat
本身就可以工作for
。
编辑
有趣的是,如果我省略该-v
标志,循环就会执行它应该执行的操作并列出所有以.bat
.
请随意编辑问题标题,因为我不确定问题是什么。
我在用着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*
命令正在运行,但不在 for 循环中。
$ 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
mike@pc /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
答案1
您的代码中有一些错误:
使用不带引号的命令替换 ( $(...)
) 而不进行设置$IFS
split+glob 运算符将扩展不加引号。默认是按空格、制表符和换行符分割。在这里,您只想在换行符上拆分,因此需要将 IFS 设置为该值,否则这意味着如果文件名包含空格或制表符,则无法正常工作
使用不带引号的命令替换,不带set -f
.
split+glob 运算符将扩展不加引号。在这里,您不需要通配符,即通配符的扩展,例如扩展scala*
到匹配文件列表中。当您不希望 shell 进行通配时,您必须使用以下命令禁用它set -f
ls
别名为ls -F
由于您已ls
使用别名,上述问题变得更加严重ls -F
。它添加/
到目录和*
可执行文件。因此,通常,因为scala
是可执行文件,ls -F
并且scala*
作为一种通配模式,它会扩展到以它开头的所有文件名,scala
这解释了为什么它似乎egrep -v
没有过滤掉文件。
假设文件名不包含换行符
换行符与文件名中的任何字符一样有效。所以ls
解析通常不起作用的输出。例如,ls
包含a
和b
文件的目录中的输出与包含名为 的文件的目录中的输出相同a\nb
。
上面egrep
将过滤文件名的行,而不是文件名
使用egrep
代替grep -E
egrep
已弃用。grep -E
是标准等效值。
不转义.
正则表达式运算符。
上面,您曾经egrep
启用过扩展正则表达式,但不使用任何扩展 RE 特定运算符。您使用的唯一 RE 运算符是.
匹配任何字符,虽然看起来这不是您想要的。所以你可能也用过grep -F
这里。或者使用grep -v '\.bat'
.
不将正则表达式锚定在行尾
egrep .bat
将匹配包含后跟 的任何字符的任何行bat
,因此该正则表达式表示bat
不包含在第一个位置的任何内容。本来应该是的grep -v '\.bat$'
。
$f
不加引号
不加引号的扩展是 split+glob 运算符。在那里,您两者都不想要,因此$f
应该用引号引起来 ( "$f"
)。
使用echo
echo
扩展其参数中的 ANSI C 转义序列和/或-n
根据-e
实现echo
(和/或环境)特殊对待字符串。
所以更好的解决方案:
for f in *; do
case $f in
(*.bat);;
(*) printf '%s\n' "$f"
esac
done
但如果当前目录中没有非隐藏文件,仍然会输出*
.您可以通过zsh
更改*
为*(N)
或bash
通过运行来解决该问题shopt -s nullglob
。
答案2
您不应该使用ls
这种方式解析文件。
首先设置 extglob globstarshopt -s extglob globstar
然后
for f in !(*.bat)
do
printf '%s\n' "$f"
done
使用find
find . -type f ! -name '*.bat'
使用否定运算符可以更安全地处理文件。
答案3
你的 for 循环本质上没有任何问题:
$ for f in $(ls | egrep -v .bat); do echo $f; done
这是我对上述命令的输出:
$ for f in $(ls | egrep -v .bat); do echo $f; done
fsc
scala
scalac
scaladoc
scalap
可能的修复
一个建议是引用 的参数egrep
,如下所示:
$ for f in $(ls | egrep -v '.bat'); do echo $f; done
在 Bash 中编写脚本时,还可以查看 Bash Pitfalls wiki,了解其他潜在问题。特别是这听起来像最合理的理由为什么你会遇到这个问题。检查返回的任何文件是否包含空格。
调试
另一件要尝试的事情是在运行 bash 命令之前启用它的详细调试,以便您可以看到幕后发生的情况。
这可以实现调试:
$ set -x
然后运行你的命令:
$ 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
然后禁用它:
$ set +x
CYGWIN 有问题吗?
鉴于这些示例在许多本机 Linux 系统上运行良好,问题很可能根源于与 Cygwin 和/或其特定版本的bash
和 有关egrep
。我会特别注意 Bash 中的字段分隔符,看看 Windows ( ) 与 Unix ( )$IFS
上的各种行分隔符之间是否存在问题。0x0d,0x0a
0x0a
我没有安装 Cygwin,所以我没有办法证明这个假设。