В zsh ** /
glob означает «на любом уровне вложенности каталогов»:
grep 'interesting string' **/myfolder/myfile.out
В bash: shopt -s globstar
затем как указано выше. Помните, что в bash ≤4.2 это пересекает символические ссылки на каталоги. Эта функция еще не существует в древней версии bash для OSX.
В ksh93: установите -G
, затем как указано выше.
Это может привести к сбою, если общая длина имен файлов слишком велика. В этом случае вы можете вернуться к вызову find
, который запустит grep
в пакетном режиме:
find -path '*/myfolder/myfile.out' -exec grep -H 'interesting string' {} +
или, если вы не можете полагаться на инструменты GNU:
find -name myfolder -exec sh -c 'grep "$0" /dev/null "$1/myfile.out"' 'interesting string' {} \;
хотя, если вы Если есть zsh, то он снова приходит на помощь с zargs
:
zargs -- **/myfolder/myfile.out -- grep 'interesting string' /dev/null
Другой подход - использовать другой инструмент, который сочетает grep и обход каталогов более изящными способами, чем GNU grep. Например, вы можете использовать серебряный поисковик :
ag -G '/myfolder/myfile\.out$' 'interesting string'
А вот этот:
find. -name '*.postinst' | xargs -I something bash -c 'git ls-files | grep $(basename something)'
Следуя подсказке git ls-files
, я создал короткий скрипт для запуска команды и фильтрации ее вывода с помощью comm
. Тонкости заключаются в том, что comm
требует отсортированного ввода, а строки должны быть точным текстовым соответствием (, чтобы не было префиксов, таких как ./
, которые типичная команда find
может создать ).
#!/bin/bash
set -eu
usage()
{
cat <<EOF
Usage:
$0 cmd [args... ]
Runs _cmd_ (with arguments, if provided) and filter the output to
files currently tracked by Git.
Example:
$0 find. -name README -mmin -60
EOF
}
trap 'usage >&2' ERR
case "${1:-}" in
-h|--help|'-?')
usage
exit 0
;;
'')
usage >&2
exit 1
;;
esac
exec comm -12 \
<("$@" | xargs -d '\n' -n 1 realpath -s --relative-to=. | sort) \
<(git ls-files | sort)
Если переключение на zsh
является опцией, вы можете иметь список отслеживаемых файлов в массиве:
tracked_files=(${(0)"$(git ls-files -z)"})
А затем выполните пересечение списка интересующих вас файлов, например:
postinst_files=(**/*.postinst)
tracked_postinst_files=(${postinst_files:*tracked_files})
Вы можете делать с zsh globs почти все, что можно делать с find
. Например, если вы хотите, чтобы только обычные файлы были изменены за последние 2 часа:
postinst_files=(**/*.postinst(.mh-2))
Или вы можете напрямую искать свой шаблон в списке отслеживаемых файлов:
tracked_postinst_files=(${(M)tracked_files:#*.postinst})
Или определите функцию квалификатора glob, которая проверяет, находится ли файл в списке отслеживаемых файлов:
istracked() ((${tracked_files[(Ie)${1-$REPLY}]}))
tracked_postinst_files=(**/*.postinst(+istracked))