Как использовать grep для всех файлов нерекурсивно в каталоге?

35

Я хочу найти строку текста во всех файлах в каталоге (а не в его подкаталогах; я знаю, что -rопция делает это, но это не то, что я хочу).

  1. Бег

    grep "string" /path/to/dir
    

    я должен был это сделать, я читал, но это дает мне ошибку:

    grep: dir: это каталог

  2. Затем я попытался запустить grepнесколько файлов.

    grep "string" .bashrc .bash_aliases работает отлично.

    grep "string" .bash* работает как задумано тоже.

    grep "string" * дает мне ошибки:

    grep: data: Is a directory
    grep: Desktop: Is a directory
    grep: Documents: Is a directory
    grep: Downloads: Is a directory
    ...
    

Только ошибки напечатаны, я не получаю соответствующие строки. Я пытался использовать -sопцию, но безрезультатно.

Итак, мои вопросы:

  1. Почему я не могу использовать grepкаталог, как в (1), когда я должен это сделать? Я видел это в большом количестве примеров в Интернете.
    Редактировать : когда я говорю «использование grep в каталоге», я имею в виду «поиск во всех файлах в этом каталоге, за исключением его подкаталогов». Я считаю, что это то, что делает grep, когда вы передаете ему каталог вместо файла. Я не прав?

  2. Пожалуйста, дайте мне объяснение о том, как grepэто могло бы объяснить поведение команд в (2).
    Изменить : Позвольте мне быть более конкретным. Почему использование подстановочных знаков для указания нескольких файлов для поиска для работы, .bash*а не с *или даже ./*?

  3. Как я могу найти все файлы в каталоге (а не в его подкаталогах), используя grep?

Джон Ред
источник
Кроме того, вы полагаетесь на подстановочные знаки расширения оболочки, такие как *, называемые globbing. Глобирование не включает имена файлов, начинающиеся с точки, такой .bashrcкак стандарт. Вы можете установить параметры оболочки так, чтобы она включала эти файлы, но вы можете немного запутаться, если не знаете, что делаете. Хорошее руководство по пониманию глобинга можно найти здесь mywiki.wooledge.org/glob
Arronical
Я не знаю, почему, но я всегда делал скрытые файлы, и это всегда работало. Я не менял никаких настроек или чего-то еще. Как я указал в (2), он grep "string" .bash*тоже работает .
Джон Ред
Извините, мой последний пример был неверным. Вы также можете искать в скрытых файлах, подавляя «каталог», потому что Linux технически рассматривает каталоги как файл другого типа. Команда будет тогда: grep "string" * .* 2>/dev/nullилиgrep -s "string" * .*
Терренс
Также это stackoverflow.com/q/9217185/3701431
Сергей Колодяжный

Ответы:

41

В Bash глобус не будет расширяться до скрытых файлов, поэтому, если вы хотите искать все файлы в каталоге, вам нужно указать скрытые .*и не скрытые файлы *.

Чтобы избежать ошибок «Является ли каталог», вы могли бы использовать -d skip, но в моей системе я также получаю ошибку grep: .gvfs: Permission denied , поэтому я предлагаю использовать -s, которая скрывает все сообщения об ошибках.

Итак, команда, которую вы ищете:

grep -s "string" * .*

Если вы ищете файлы в другом каталоге:

grep -s "string" /path/to/dir/{*,.*}

Другим вариантом является использование параметра dotglobоболочки, который будет включать в себя скрытые файлы.

shopt -s dotglob
grep -s "string" *

Для файлов в другом каталоге:

grep -s "string" /path/to/dir/*

† Кто-то упомянул, что я не должен получать эту ошибку. Возможно, они правы - я немного почитал, но сам не мог понять, что с ним делать.

wjandrea
источник
Есть ли причина для промежутка между *и .*?
Хашим
2
@Hashim Сравните вывод echo * .*и echo *.*запустите в вашем домашнем каталоге, и разница должна быть очевидной. Иначе ЛМК и я это объясню.
wjandrea
Интересно, что echo *показывает не скрытые файлы и папки, echo *.*показывает не скрытые файлы, echo .*показывает все файлы и echo * .*показывает все файлы и каталоги. Но почему причина пространства между двумя в последнем случае? Мне это кажется грязным. Разве нет способа объединить два, чтобы получить одинаковые результаты? Или в противном случае есть синтаксическое объяснение, почему эти два должны быть разделены здесь, или * .*это исключительный случай?
Хашим
1
@ Хашим, я не уверен, как ты пришел к таким выводам, поэтому позвольте мне объяснить. Во-первых, каталоги являются файлами в этом контексте. В globs *представляет все не скрытые файлы (т.е. имена файлов, которые не начинаются с точки); .*представляет все скрытые файлы (т.е. имена файлов , которые действительно начинаются с точкой); и *.*представляет все не скрытые файлы, которые содержат точку. В echo * .*этом случае два глоба должны быть разделены, потому что это разные глобусы: один для скрытого, другой для скрытого. Хотя, как я написал в своем ответе, вы можете *включить скрытые файлы, включив dotglobопцию оболочки.
wjandrea
1
Обычно используется *.*в Windows (DOS) для вывода списка всех файлов, но в * nix будут включаться только файлы с точкой, поэтому в * nix это не имеет смысла. Вместо этого вы используете *список всех файлов, кроме скрытых, и .*список скрытых файлов.
Томасруттер
11

Вам нужно -d skipдобавить опцию.

  1. Grep ищет внутри файлов. Как вы сказали, вы можете искать рекурсивно, если вы хотите искать файлы внутри каталога.

  2. По умолчанию grep прочитает все файлы и обнаружит каталоги. Поскольку по умолчанию вы не определили, что делать с каталогами с помощью -dопции, она выдаст ошибку.

  3. Поиск только в родительском каталоге будет grep -d skip "string" ./*

anonymous2
источник
Для получения дополнительной информации о grep, см man grep.
anonymous2
(а) Пожалуйста, смотрите редактирование. (б) Использование -d skipне работает; это в основном так же, как -s; также см. редактирование. (с) Нет, grep -d skip "string" ./*тоже не работает.
Джон Ред
7

Старые таймеры, вероятно, сделали бы это:

find . -type f -print0 | xargs -0 grep "string"
dathompson
источник
3
Почему нет find . -type f -exec grep string {} +?
wchargin
5
Вы тоже хотите -maxdepth 1.
wchargin
@wchargin: если вам нужно имя файла в выводе, когда есть только один файл, я думаю, что вы хотитеfind . -type f -maxdepth 1 -exec grep string /dev/null {} +
Грегори Нисбет
1
@GregoryNisbet: просто перейдите -Hк grep.
wchargin
2

Перефразируя - вы хотите grep файлы в одном уровне подкаталога, но не повторять через все подкаталоги?

grep forthis  *  */*

Или, если вы не хотите файлы в текущем каталоге

grep forthis  */*

Обратите внимание, что каталоги не будут начинаться с точки.

grep forthis  .*/*    */*   

должен сделать эту работу.

Там также -maxdepthи -mindepthпараметры доступны ограничение на findкоманды тоже.

Criggie
источник
Не будет ли grep forthis */*искать файлы как в текущем каталоге, так и в одном из них?
Хашим
@ Хашим, в основном, нет, потому что */*сопоставляет только одну косую черту. Если у вас есть файл с именем a/bв текущем каталоге, то `* / * будет соответствовать этому.
Кригги
0

Вот пример, чтобы пропустить каталоги, не пропуская все ошибки:

grep --directories='skip' 'searchString' *
Мойтаба Резаян
источник