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 -lgrep -c.

快得多

对于 Bash 4,它有 globstarreadarray,一个纯粹的 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.

的文件