Shell 显示所有 1 行 php 文件的脚本
Shell script to show all 1 line php files
正在尝试创建 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
awk
救援!
for f in *.php; do awk 'END{ if(NR==1) print FILENAME}' $f; done
对于您需要使用查找的递归查找,一种替代方法可以是
find -name *.php -print | xargs -L1 awk 'NR>1{exit} END{if(NR==1) print FILENAME}'
这将递归搜索单行 PHP 文件:
find -name '*.php' -exec bash -c '[[ "$(wc -l < "[=10=]")" -eq 1 ]] && echo "[=10=]"' '{}' ';'
如果要在if
语句中测试命令的成功或失败,则不要使用[
运算符,而是将其写在[=13=之后]直接:
if find . -type f | wc -l $f == 1; then
echo "$f"
fi
然而,我上面写的仍然没有多大意义,不是我想你想要的。
[
本身是一个用于将算术和字符串比较转换为if
友好形式的命令。您可以将它与输出替换结合使用(使用 $(...)
语法)来测试命令的输出是否等于 1。
if [ "$(wc -l < "$f")" -eq 1 ]; then
echo "$f"
fi
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"
这将找到 "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
表示 "Always print the filename, only print the count of the matches, and match at most two lines." 模式 $
匹配任何行,而模式 "[^[:space:]]"
匹配任何包含非空白字符的行。以 {} +
结束 -exec
告诉 find
提供文件列表而不是在每个文件上触发 exec
,这样效率更高。最后,sed
打印以 :1
结尾的行(在删除 :1
之后),这将是包含非空白字符的行数的文件的文件名正好一个。
这可以说比 wc
更有效,因为它通常在第二行停止读取,而不是读取整个文件只是为了检查它们是否有多于一行。
(另外,关于 wc
:如果文件恰好只有一行,但该行没有以换行符结尾,那么 wc
将报告它有 0行。所以如果你过滤 wc 输出等于 1,你可能会错过一些文件。)
如果您的 bash 比较新,您可以通过启用 **
glob 来避免 find
:
shopt -s globstar nullglob
grep -Hcm2 "[^[:space:]]" **/*.php | sed -n '/:1$/{s///;p}'
None 如果您的文件路径中包含换行符,则上述 hacks 中的一些有效。但你不知道,对吧? :-)
Bash 4 提供了 "globstar" shell 选项来处理你的递归需求:
shopt -s globstar
wc -l **/*.php | awk '==1' | sed 's/^ *[0-9]* *//'
或者如果你不喜欢 sed,你可以把更多的东西放到管道的 awk 部分:
wc -l **/*.php | awk '==1{sub(/^ *[0-9]+ */,"");print}'
这两种解决方案以及(此时)发布的所有其他解决方案都存在长文件将被完整读取的问题。您实际上并不需要这样做——如果您看到其中有第二行,您可以跳过任何文件。因此,再次依赖 Bash 4 的 "globstar" 进行递归:
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
.
快得多
对于 Bash 4,它有 globstar
和 readarray
,一个纯粹的 Bash 解决方案相当简单:
shopt -s globstar nullglob dotglob # nocaseglob
for phpfile in **/*.php ; do
readarray -n 2 lines < "$phpfile"
[[ ${#lines[*]} == 1 ]] && printf '%q\n' "$phpfile"
done
处理带有任何合法字符(包括换行符)的文件名,以及以点开头的文件名。它计算行未终止的单行文件(与使用 wc
的解决方案不同)。它最多从每个文件中读取两行,因此如果遇到许多大文件,它的速度应该不会太慢。使用带有 printf
的 %q
格式意味着可以在 shell 命令中安全地使用输出而无需额外的引号,甚至名称中包含换行符的文件也会打印在一行上。取消注释 nocaseglob
以计算后缀为 .PHP
或 .Php
.
的文件
正在尝试创建 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
awk
救援!
for f in *.php; do awk 'END{ if(NR==1) print FILENAME}' $f; done
对于您需要使用查找的递归查找,一种替代方法可以是
find -name *.php -print | xargs -L1 awk 'NR>1{exit} END{if(NR==1) print FILENAME}'
这将递归搜索单行 PHP 文件:
find -name '*.php' -exec bash -c '[[ "$(wc -l < "[=10=]")" -eq 1 ]] && echo "[=10=]"' '{}' ';'
如果要在if
语句中测试命令的成功或失败,则不要使用[
运算符,而是将其写在[=13=之后]直接:
if find . -type f | wc -l $f == 1; then
echo "$f"
fi
然而,我上面写的仍然没有多大意义,不是我想你想要的。
[
本身是一个用于将算术和字符串比较转换为if
友好形式的命令。您可以将它与输出替换结合使用(使用 $(...)
语法)来测试命令的输出是否等于 1。
if [ "$(wc -l < "$f")" -eq 1 ]; then
echo "$f"
fi
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"
这将找到 "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
表示 "Always print the filename, only print the count of the matches, and match at most two lines." 模式 $
匹配任何行,而模式 "[^[:space:]]"
匹配任何包含非空白字符的行。以 {} +
结束 -exec
告诉 find
提供文件列表而不是在每个文件上触发 exec
,这样效率更高。最后,sed
打印以 :1
结尾的行(在删除 :1
之后),这将是包含非空白字符的行数的文件的文件名正好一个。
这可以说比 wc
更有效,因为它通常在第二行停止读取,而不是读取整个文件只是为了检查它们是否有多于一行。
(另外,关于 wc
:如果文件恰好只有一行,但该行没有以换行符结尾,那么 wc
将报告它有 0行。所以如果你过滤 wc 输出等于 1,你可能会错过一些文件。)
如果您的 bash 比较新,您可以通过启用 **
glob 来避免 find
:
shopt -s globstar nullglob
grep -Hcm2 "[^[:space:]]" **/*.php | sed -n '/:1$/{s///;p}'
None 如果您的文件路径中包含换行符,则上述 hacks 中的一些有效。但你不知道,对吧? :-)
Bash 4 提供了 "globstar" shell 选项来处理你的递归需求:
shopt -s globstar
wc -l **/*.php | awk '==1' | sed 's/^ *[0-9]* *//'
或者如果你不喜欢 sed,你可以把更多的东西放到管道的 awk 部分:
wc -l **/*.php | awk '==1{sub(/^ *[0-9]+ */,"");print}'
这两种解决方案以及(此时)发布的所有其他解决方案都存在长文件将被完整读取的问题。您实际上并不需要这样做——如果您看到其中有第二行,您可以跳过任何文件。因此,再次依赖 Bash 4 的 "globstar" 进行递归:
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
.
对于 Bash 4,它有 globstar
和 readarray
,一个纯粹的 Bash 解决方案相当简单:
shopt -s globstar nullglob dotglob # nocaseglob
for phpfile in **/*.php ; do
readarray -n 2 lines < "$phpfile"
[[ ${#lines[*]} == 1 ]] && printf '%q\n' "$phpfile"
done
处理带有任何合法字符(包括换行符)的文件名,以及以点开头的文件名。它计算行未终止的单行文件(与使用 wc
的解决方案不同)。它最多从每个文件中读取两行,因此如果遇到许多大文件,它的速度应该不会太慢。使用带有 printf
的 %q
格式意味着可以在 shell 命令中安全地使用输出而无需额外的引号,甚至名称中包含换行符的文件也会打印在一行上。取消注释 nocaseglob
以计算后缀为 .PHP
或 .Php
.