我正在尝试查找所有具有扩展名的文件.md
find . -type f -name "*.md"
然后我想过滤掉那些第二行(行号= 2)不包含以开头和结尾的确切字符串的文件author: Mr. Xab Ycd
我该如何做第二部分?这样做grep
效率很低,因为它会检查整个文件。
答案1
find . -type f -name '*.md' -exec \
sh -c 'sed 1d\;q "$1" | grep -qvx "author: Mr. Xab Ycd"' sh {} \; -print
上述命令包含所有要求,而无需通过 shell 管道传递可能令人困惑的文件名。
第一部分是按原样(几乎)从您的复制的 - 查找名为*.md
.我已将双引号更改为“硬”单引号;您的情况没有功能差异,但如果您想查找名为*.$md
then 双引号的文件,则会尝试扩展变量$md
.
然后匹配的文件名通过另一个测试通过-exec
。 exec 的参数是一个小的 shell 脚本,其作用是确定 .txt 中给定文件名的成功或失败$1
。该sed
命令仅打印第二行;有不同的方法可以做到这一点,例如:
sed -n '2{p;q;}'
或者sed '1d;q
第一个说“默认情况下不打印行,但是当您看到第二行时,打印它然后退出”。第二个表示“默认打印行,但删除第一行,然后退出(在第二行);该q
命令将在退出之前打印当前缓冲区。
该行文本(如果有)被传递给 grep,后者检查整行是否与给定文本匹配(或不匹配)。如果是的话不是match ( -v
),则整个命令成功并find
打印文件名。
答案2
find . -type f -name '*.md' -exec awk '
FNR == 2 && $0 == "author: Mr. Xab Ycd" { exit 1 }
FNR > 2 { exit 0 }' {} ';' -print
这将用于awk
过滤掉至少两行长且第二行正是您提到的字符串的任何文件。如果第二行 ( FNR == 2
) 与字符串完全相同,它会通过显式退出并以非零退出状态来实现此目的。如果到达第二行之后的任何行,它也会以零退出状态退出,以免解析不必要的内容。
如果以零退出状态退出(在第二行未找到该字符串),该find
命令将继续打印文件的路径名。-print
awk
答案3
和zsh
:
by_Xab() {
local line
{
IFS= read -r line &&
IFS= read -r line &&
[[ $line = "author: Mr. Xab Ycd" ]]
} < ${1-$REPLY}
}
printf '%s\n' **/*.md(D.^+by_Xab)
每个文件最多读取 2 行,并且不执行任何命令(都是内置命令),因此比find -exec
每个文件运行一个或多个命令的方法要高效得多。
使用 GNU awk
,你可以这样做:
STRING='author: Mr. Xab Ycd' find . -name '*.md' -type f -exec gawk '
BEGINFILE {found = 0}
FNR == 2 {found = $0 == ENVIRON["STRING"]; nextfile}
ENDFILE {if (!found) print FILENAME}' {} +
它将运行一次find
调用,并使用尽可能-exec ... {} +
少的gawk
调用语法。
答案4
当find
一次提供一个文件并且打印的责任在于find
:
find . -type f -exec perl -lne '$. == 2 && exit +/^author: Mr\. Xab Ycd$/' {} \; -print
这里find
提供一堆文件,打印任务由以下命令处理perl
:
find . -type f -size 0 -print -o -exec perl -lne '
print $ARGV if $. == 2 && !/^author: Mr\. Xab Ycd$/;
close(ARGV),next if $. == 2;
print($ARGV),close(ARGV) if eof;
' {} +
这关闭-英关闭ARGV是必不可少的 OTW 线路计数器,又名,$。未针对即将到来的文件进行初始化。
请注意,需要 eof 子句,对于长度 > 1 的文件,永远不会到达第二行,并且永远不会检查第二行。