
У меня есть каталог с именем "labels", в котором находятся текстовые файлы, содержащие метки для "cat" или "dog" или обоих на отдельных строках.
Содержимое файлов в каталоге labels следующее:
cat labels/1.txt
cat
cat labels/2.txt
dog
cat labels/3.txt
cat
dog
Я хочу получить имена файлов, которые содержат только метку "cat". Я попробовал следующую команду:
ls labels | grep -Rwl "cat"
labels/1.txt
labels/3.txt
Но эта команда возвращает имена тех файлов, которые содержат "cat" или оба. Но мне нужно получить те имена файлов, которые содержат только "cat", а не оба "cat" и "dog".
Аналогично, когда я пытаюсь получить имена тех файлов, которые содержат только "dog". Если я ищу таким же образом, то она возвращает имена файлов, которые содержат "dog" или обе метки.
ls labels | grep -Rwl "dog"
labels/2.txt
labels/3.txt
решение1
Вы можете использовать grep
дважды: а) для перечисления всех файлов с cat
, затем б) отсеять dog
те, которые содержат . Используйте -l
и -L
, соответственно, где -l
перечисляет имена файлов с совпадениями и -L
имена файлов без совпадений:
grep -L 'dog' $(grep -l 'cat' <list of files>)
Видеть man grep
:
-L, --files-without-match
Подавить нормальный вывод; вместо этого вывести имя каждого входного файла, из которого обычно не печатается вывод. Сканирование остановится при первом совпадении.
-l, --files-with-matches
Подавить нормальный вывод; вместо этого вывести имя каждого входного файла, из которого вывод обычно печатался бы. Сканирование остановится при первом совпадении.
решение2
С GNU grep
и xargs
( -R
который вы уже используете, grep
в любом случае является расширением GNU, хотя -r
там он предпочтительнее):
grep -rwlZ cat labels/ | xargs -r0 grep -wL dog
Выводит список файлов, содержащих хотя бы одно cat
слово и не содержащих ни dog
одного слова (словов этом контексте означает: «не окруженныйсимволы слова",символы слова(буквенно-цифровые символы и подчеркивание). Заменить -w
на -x
для поиска строк, все содержимое которыхявляется cat
/ dog
.
решение3
Если вы хотите вывести список имен файлов, содержащих «cat», но не содержащих «dog», попробуйте сделать что-то вроде этого, используя find
и GNU awk
(или любую другую awk
, которая поддерживает ENDFILE
блоки, поскольку это расширение GNU для awk
):
$ find labels/ -type f -exec awk -v IGNORECASE=1 '
/\<cat\>/ { cat = 1 };
/\<dog\>/ { dog = 1 };
ENDFILE {
if (cat == 1 && dog == 0) {
print FILENAME
};
cat = 0;
dog = 0;
}' {} +
labels/file1.txt
Или вы можете использовать perl
вместо awk
:
$ find labels/ -type f -exec perl -l -n -e '
$cat = 1 if m/\bcat\b/i;
$dog = 1 if m/\bdog\b/i;
if (eof) {
print $ARGV if ($cat && ! $dog);
$cat=0;
$dog=0;
}' {} +
labels/file1.txt
Вывод обеих версий awk и perl, представленных выше, был получен со следующими файлами в labels/
подкаталоге:
$ tail labels/*
==> labels/file1.txt <==
cat
==> labels/file2.txt <==
dog
==> labels/file3.txt <==
cat
dog
labels/file1.txt
— единственное выведенное имя файла, поскольку это единственный файл, содержащий слово «cat» и не содержащий «dog».
решение4
for f in *; do diff -q <(sort -u "$f") <(echo cat) >/dev/null && echo "$f"; done