尝试创建一个shell脚本以递归方式搜索目录,并显示仅包含1行的所有php文件的列表
我认为我的IF声明有问题,但我不确定
#!/bin/bash
shopt -s nullglob
for f in *.php
do
if [ 'find . -type f | wc -l $f == 1' ];
then echo "$f"
fi
答案 0 :(得分:2)
这将找到" one-liners":
find . -type f -name '*.php' -exec grep -Hcm2 $ {} + | sed -n '/:1$/{s///;p}'
这个会找到" one-liners"当除了一行之外的所有行都是空白时,碰巧有多行;
find . -type f -name '*.php' -exec grep -Hcm2 '[^[:space:]]' {} + |
sed -n '/:1$/{s///;p}'
grep选项-Hcm2
表示"始终打印文件名,只打印匹配的计数,并且最多匹配两行。"模式$
匹配任何行,而模式"[^[:space:]]"
匹配包含非空白字符的任何行。使用-exec
结束{} +
告诉find
提供文件列表,而不是在每个文件上触发exec
,这样效率会更高。最后,sed
打印以:1
结尾的行(在删除:1
之后),这将是包含非空白字符的行数的文件的文件名。一个。
这可以说比wc
更有效率,因为它通常会停止在第二行读取,而不是仅仅为了检查它们是否有多行来读取整个文件。
(另外,对于wc
:如果文件恰好只有一行,但该行没有以换行符结尾,则wc
将报告它有0行。因此,如果您将wc输出的等式过滤为1,则可能会遗漏几个文件。)
如果你有一个合理的最近bash,你可以通过启用find
globs来避免**
:
shopt -s globstar nullglob
grep -Hcm2 "[^[:space:]]" **/*.php | sed -n '/:1$/{s///;p}'
如果您的文件路径中包含换行符的文件,则上述黑客都不起作用。但你不是吗,对吧? : - )
答案 1 :(得分:1)
awk
救援!
for f in *.php; do awk 'END{ if(NR==1) print FILENAME}' $f; done
对于需要使用find的递归查找,可以使用一种替代方法
find -name *.php -print | xargs -L1 awk 'NR>1{exit} END{if(NR==1) print FILENAME}'
答案 2 :(得分:1)
这将以递归方式搜索单行PHP文件:
find -name '*.php' -exec bash -c '[[ "$(wc -l < "$0")" -eq 1 ]] && echo "$0"' '{}' ';'
如果要在if
语句中测试命令的成功或失败,则不要使用[
运算符,而是直接在if
之后写入:
if find . -type f | wc -l $f == 1; then
echo "$f"
fi
然而,我上面写的内容仍然没有多大意义,也不是我认为的意思。
[
本身是一个用于将算术和字符串比较转换为if
友好形式的命令。您可以将它与输出替换(使用$(...)
语法)结合起来,以测试命令的输出是否等于1.
if [ "$(wc -l < "$f")" -eq 1 ]; then
echo "$f"
fi
答案 3 :(得分:1)
Bash 4提供了&#34; globstar&#34; shell选项来处理递归需求:
shopt -s globstar
wc -l **/*.php | awk '$1==1' | sed 's/^ *[0-9]* *//'
或者如果你不喜欢sed,你可以把更多的东西放到管道的awk部分:
wc -l **/*.php | awk '$1==1{sub(/^ *[0-9]+ */,"");print}'
这两个解决方案,以及(此时)所发布的所有其他解决方案都会受到影响,因为长文件将被完整读取。你真的不需要这样做 - 你可以跳过任何文件,如果你看到第二行。所以,再次依靠Bash 4&#34; globstar&#34;递归:
awk 'FNR==1{a[FILENAME]} FNR==2{delete a[FILENAME];nextfile} END{for(f in a){print f}}' **/*.php
扩展以便于解释,这是:
awk '
# Upon reading the first line of a file, add its name to an array.
FNR==1 {a[FILENAME]}
# Upon reading the second line, delete it from the array and move on.
FNR==2 {delete a[FILENAME];nextfile}
# Once we've processed all files, print what's left in the array.
END {for(f in a){print f}}
' **/*.php
这应该从每个文件最多读取两行。可能比wc -l
或grep -c
快得多。
答案 4 :(得分:0)
使用包含globstar
和readarray
的Bash 4,纯粹的Bash解决方案非常简单:
shopt -s globstar nullglob dotglob # nocaseglob
for phpfile in **/*.php ; do
readarray -n 2 lines < "$phpfile"
[[ ${#lines[*]} == 1 ]] && printf '%q\n' "$phpfile"
done
处理带有任何合法字符(包括换行符)的文件名,以及以dot开头的文件名。它计算行未终止的单行文件(与使用wc
的解决方案不同)。它从每个文件中最多读取两行,因此如果它遇到许多大文件,它不应该减慢很多。将%q
格式与printf
一起使用意味着可以安全地在shell命令中使用输出而无需额外引用,甚至名称中包含换行符的文件也会打印在一行中。取消注释nocaseglob
以计算带有后缀的文件,例如.PHP
或.Php
。
答案 5 :(得分:-1)
for FILE in $(find . -type f -name *.php)
do
if [ $(wc -l $FILE) -eq 1 ]
then
echo $FILE
fi
done
应该做的伎俩,尽管它有点笨拙。
更优雅的是
find . -type f -name '*.php' -exec wc -l {} \; | egrep "^\s*1\s"