grep не выдает никаких результатов для каталога, переданного в переменной

grep не выдает никаких результатов для каталога, переданного в переменной

Я пытаюсь написать bashскрипт, который ищет содержимое файлов в указанном дереве каталогов на наличие указанной подстроки.

Использование grepтолько рекурсивной функции недостаточно, поскольку мне потенциально нужно перебрать каталог /(и все подкаталоги) системы, что приведет к grepисчерпанию памяти и прерыванию. Поэтому я решил получить список всех каталогов и подкаталогов в указанном дереве каталогов, используя findследующие переменные, обозначающие аргументы, передаваемые скрипту.

searchdir=$HOME     # passed in a script argument
searchstr="secret"  # passed in a script argument

Я вызываю findутилиту и сохраняю вывод во временный файл.

TF=$(mktemp)
find ${searchdir} -type d 1>$TF 2>/dev/null

Имея список всех каталогов во временном файле, я продолжаю итерацию по строкам этого файла, используя while-doцикл с намерением выполнить поиск по всем файлам в каждом каталоге. Для grepя использую формат параметров, предоставленный вэтот ответдля поиска всех файлов, включая скрытые, в одном каталоге.

cat $TF | while read line || [[ -n $line ]];
do
    grepdir="${line}/{*,.*}"
    grep -sHn "${searchstr}" ${grepdir}
done

... однако этот код не выводит никаких данных.

Я убедился, что...

Содержит ${TF}правильный список всех каталогов. Вывод ${grepdir}переменной дает вывод, который я ожидаю найти.

/home/user/{*,.*}
/home/user/.ssh/{*,.*}
/home/user/test/{*,.*}
# ... and so on

Если я запускаю grepкоманду с жестко заданным каталогом, в частности ~/test/, каталогом, содержащим два тестовых файла со строкой, которую она должна найти

grep -sHn "${searchstr}" /home/user/test/{*,.*}

... он правильно выводит два файла, содержащие подстроку «секрет».

/home/user/test/asdf:7:secret
/home/user/test/test.txt:5:asdfasfdsecretaasdfafd

Формат, который мне подходит, — это тот, который изначально упоминался вответ, обсуждающий рекурсивное использованиеgrep. Если я сделаю это:

cat $TF | while read line || [[ -n $line ]];
do
    grep -rn "${line}" -e "${searchstr}"
done

... Я получаю некоторый вывод (технически правильный, но со множеством дубликатов записей), но поскольку grepобрабатывает каталоги рекурсивно, а у меня есть список всех каталогов, я обязательно буду получать одни и те же результаты много раз, а в таких каталогах, как вышеупомянутый корневой каталог, grepпроизойдет полный сбой, чего я и пытаюсь избежать.


Вероятно, мне также следует упомянуть, что мои отчаянные попытки заставить это работать, такие как передача $(echo "${grepdir}")в качестве параметра, также не привели к каким-либо результатам.

Скорее всего, в моем мышлении или понимании есть заблуждение bash. Не следует ли bashрасширить ${grepdir}переменную перед вызовом grep? Где мой скрипт идет не так?

решение1

Правило №1: Если команда или скрипт не выполняет то, что вам нужно, просмотрите сообщения об ошибках.  Не бросайте их в  /dev/null.

Вы получаете сообщения об ошибках, подобные

grep: /home/user/{*,.*}: No such file or directory
grep: /home/user/.ssh/{*,.*}: No such file or directory
grep: /home/user/test/{*,.*}: No such file or directory

но вы их не видите.

Если мы посмотрим набаш(1), мы видим

Расширение выполняется в командной строке после ее разделения на слова. Существует семь видов выполняемого расширения: расширение фигурных скобок, расширение тильды, расширение параметров и переменных, подстановка команд, арифметическое расширение, разделение слов и расширение имени пути.

Порядок расширений следующий: расширение фигурных скобок; расширение тильды, расширение параметров и переменных, арифметическое расширение и подстановка команд (выполняется слева направо); разделение слов; и расширение имени пути.

Важной частью для вашей ситуации является то, что расширение скобок происходит до расширения переменной. Так что, если вы сказали

grep -sHn "${searchstr}" "${line}"/{*,.*}

затем

  • раскрытие скобок превратит последний токен в "${line}"/*и "${line}"/.*,
  • Расширение переменной превратит приведенное выше в /home/user/*и /home/user/.*, а затем
  • Расширение имени пути превратит указанное выше в список имен файлов.

Но когда вы говорите,

grep -sHn "${searchstr}" ${grepdir}

затем

  • Расширение переменной превращает последний токен в /home/user/{*,.*},

и тогда уже слишком поздно для раскрытия фигурных скобок.  grepищет файл с буквальным именем /home/user/{*,.*}.


ПС

grep -sHn "${searchstr}" "${line}/{*,.*}"

Это тоже не сработает, поскольку кавычки не позволят раскрыть скобки и пути.

PPS Тебе не нужны все эти брекеты;

grep -sHn "$searchstr" "$line"/{*,.*}

было бы хорошо.

решение2

Причина, по которой grep прерывается при рекурсии по всей системе, скорее всего, не в том, что он не может справиться с объемом данных, а в том, что он спотыкается об один или другой псевдофайл или файл устройства в /proc, /sys или /dev. Вы можете исключить проблемные каталоги с помощью опции --excludeв командной строке.

Причина, по которой он не расширяет подстановочные знаки, заключается в том, что они заключены в кавычки в этой строке:

    grepdir="${line}/{*,.*}"

Изменение этого, вероятно, будет способствовать их расширению.

    grepdir="${line}/"{*,.*}

Другой способ добиться этого (с меньшим количеством скриптов с вашей стороны) — выбрать файлы, используя findконвейерную передачу путей к файлам xargsдля обработки:find / ... -print 0 | xargs -0 ...

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

Связанный контент