! (Шаблон)
- это синтаксис ksh
glob, в zsh
, вы используете ^ (шаблон)
, чтобы отменить соответствие, когда extendedglob
включен:
setopt extendedglob
print -rl -- ^(2test|3test)
Если вы хотите использовать синтаксис ksh
, вам необходимо включить kshglob
:
setopt kshglob
print -rl -- !(2test|3test)
Вы также можете использовать and-not / кроме оператора :
setopt extendedglob
print -rl -- *test~[23]*
( * test
, кроме файлов, начинающихся с 2
или 3
).
Также нет, если не включена опция nobareglobqual
или вы не используете в них |
, конечные операторы группировки (...)
конфликтуют с glob квалификаторы. Например, в ! (Foo)
или ^ (foo)
, foo
будет рассматриваться как квалификатор glob. Вам понадобится ^ foo
или ! (Foo) (# q)
( (# q)
добавляет неоткрытый (явный) квалификатор glob) .
find. -type f -name '*.md' -exec \
sh -c 'sed 1d\;q "$1" | grep -qvx "author: Mr. Xab Ycd"' sh {} \; -print
Вышеупомянутая команда включает все требования без необходимости передачи потенциально -запутанных имен файлов через каналы оболочки.
Первая часть скопирована как -это (почти )из вашего --ищите файлы с именем *.md
. Я изменил двойные -кавычки на «жесткие» одинарные -кавычки; в вашем случае нет функциональной разницы, но если вы хотите найти файлы с именем *.$md
, то двойные кавычки -попытаются расширить переменную $md
.
Совпадающие имена файлов затем проходят еще один тест через -exec
. Параметр exec представляет собой небольшой сценарий оболочки, задачей которого является определение успешного или неудачного выполнения данного имени файла в $1
. Команда sed
печатает только вторую строку; есть разные способы сделать это, например:
sed -n '2{p;q;}'
или sed '1d;q
Первый говорит: «Не печатать строки по умолчанию, но когда вы увидите вторую строку, напечатайте ее и закройте». Второй говорит: «печатать строки по умолчанию, но удалить первую строку, после чего выйти (на второй строке ); команда q
перед выходом напечатает текущий буфер.
Эта строка текста (, если таковая имеется, )передается команде grep, которая проверяет, соответствует ли вся строка (или нет )заданному тексту. Если не соответствует (-v
), то вся команда выполняется успешно и, таким образом, find
печатает имя файла.
Я считаю, что awk
это правильный инструмент для работы, но я не знаю Awk . Тем не менее, следующий ответ работает с использованием Bash с включенным параметром оболочки globstar
(shopt -s globstar
).
prompt% awk 'FNR==2 {print FILENAME, $0}' **/*.md | grep -v 'author: Mr. Xab Ycd' | cut -f1 -d ' '
awk
отображает вторую строку всех файлов с именем *.md
в текущем каталоге или в подкаталогах, а grep
отфильтровывает имена файлов, не содержащие указанную строку.
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
завершается с нулевым статусом выхода (, строка не найдена во второй строке ).
С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
вызовов.
Когда 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 необходимо, так как OTW 2-я строка никогда не будет достигнута для файлов длиной > 1, а проверка 2-й строки никогда не произойдет.