--
отмечает конец опций. Здесь это лишнее (, так как имя переменной не будет начинаться с -
(, за исключением патологических случаев, когда локаль пользователя будет определена с -
в классе символов alpha )и в любом случае не добавляется при наличии флагов ), но безвреден. Я предполагаю, что он там, потому что в коде было:
printf ("declare -%s ", i ? flags : "-");
Который печатался быстрее, чем:
if (i)
printf ("declare -%s ", i ? flags : "-");
else
printf ("declare ");
По иронии судьбы, в bash-2.01
этот код был изменен на :
if (pattr == 0 || posixly_correct == 0)
printf ("declare -%s ", i ? flags : "-");
else if (i)
printf ("%s -%s ", this_command_name, flags);
else
printf ("%s ", this_command_name);
Для повторного использования для export
и readonly
, где в тот раз --
избегали для export var=value
, но не для declare -- var=value
.
Естественно, публикация вопроса помогла мне избавиться от зацикленности на том, чтобы делать это исключительно с помощью find
, и заставила задуматься о расширении файла через Bash. Я публикую ответ, надеясь, что он поможет кому-то еще (, а также задокументирует это для моего собственного будущего использования ).
Заклинание, позволяющее Bash расширить содержимое файла, звучит так: $(<subdirs2search.txt)
. Итак, если subdirs2search.txt содержит:
SubDir1 SubDir2 SubDir4
Команда, подобная следующей, выполнит нужный поиск:
find $(<subdirs2search.txt) -type f -name="*.txt" -exec grep -H "desired text" {} \;
Если имена каталогов состоят из одного -на -строку, вы можете избежать проблем с каталогами, в именах которых есть пробелы, табуляции или подстановочные знаки, используяreadarray
(bash v4+):
readarray -t dirs < subdirs2search.txt
find "${dirs[@]}"...
Это все равно не помогло бы, если имена некоторых каталогов начинаются с -
, но с GNU find
это невозможно.
#!/usr/bin/perl -w
use strict;
use File::Find ();
sub wanted;
sub process_file ($@);
my $dirfile = shift; # First argument is the filename containing the list
# of directories.
my $pattern = shift; # Second arg is a perl RE containing the pattern to search
# for. Remember to single-quote it on the command line.
# Read in the @dirs array from $dirfile
#
# A NUL-separated file is best, just in case any of the directory names
# contained line-feeds. If you're certain that could never happen, a
# plain-text LF-separated file would do.
#
# BTW, you can easily generate a NUL-separated file from the shell with:
# printf "%s\0" dir1 dir2 dir3 dir4 $'dir\nwith\n3\nLFs' > dirs.txt
my @dirs=();
{
local $/="\0"; # delete this line if you want to use a LF-separated file.
# In that case, the {... } block around the code from open to
# close is no longer needed. It's only there so it's possible
# to make a local change to the $/ aka $INPUT_RECORD_SEPARATOR
# variable.
open(DIRFILE,"<",$dirfile);
while(<DIRFILE>) {
chomp;
push @dirs, $_;
};
close(DIRFILE);
};
File::Find::find({wanted => \&wanted}, @dirs);
exit;
sub wanted {
my ($dev,$ino,$mode,$nlink,$uid,$gid);
(($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && -f _ && process_file($_);
}
sub process_file ($@) {
# This function currently just greps for pattern in the filename passed to
# it. As the function name implies, it could be used to process the file
# in any way, not just grep it.
my $filename = shift;
# uncomment the return statement below to skip "binary" files.
# (note this is a workable but fairly crude test. Perl's File::MMagic
# module can be used to more accurately identify file types, using the
# same "magic" file databases as the /usr/bin/file command)
# return if -B $filename;
open(FILE,"<",$filename);
while(<FILE>) {
print "$filename:$_" if (m/$pattern/o) ;
};
close(FILE);
}
Он использует perl
и модуль perl File::Find
, чтобы делать то же самое, что и ваш find... -exec grep
.
В этом скрипте нет ничего особенно интересного или особенного , за исключением , что функцию process_file
можно очень легко модифицировать, чтобы она делала все, что вы хотите, с файлом или с файлом -, например. изменить владельца или разрешения, удалить его, переименовать, вставить или удалить строки или что-то еще, что вы можете захотеть.
напр. если вы хотите удалить файлы, которые содержат текст, соответствующий шаблону, вы можете заменить файловую функцию процесса _чем-то вроде этого:
sub process_file ($@) {
my $filename = shift;
my $found = 0;
# uncomment to skip "binary" files:
return if -B $filename;
open(FILE,"<",$filename);
while(<FILE>) {
if (m/$pattern/o) {
$found = 1;
last;
};
};
close(FILE);
unlink $filename if ($found);
}
Стоит также отметить, что функция wanted
в этом скрипте в настоящее время ищет только обычные файлы (тест -f
). Функции Perl stat
и lstat
обеспечивают доступ ко всем метаданным файлов, которые find
могут использоваться для сопоставления файлов (uid, gid, perms, size, atime, mtime и т. д. ), поэтому wanted
функция может реплицировать ЛЮБОЙ и все предикаты поиска. подробности см. в perldoc -f stat
и perldoc -f lstat
.
Кстати, сценарий был первоначально сгенерирован find2perl
, а затем существенно изменен для )чтения списка каталогов из файла и b )для выполнения grep в коде perl, а не путем разветвления. grep
и c )добавляют много комментариев. Производительность должна быть почти идентична find... -exec grep
, потому что grep не может открывать файлы или выполнять сопоставление с шаблоном регулярного выражения значительно быстрее, чем это может сделать perl. Это может быть даже быстрее.
Кстати, find2perl
раньше включался в perl,но начиная с perl 5.22 он был удален, и теперь его можно найти на CPAN по адресу find2perl
I found that it didn't limit the search to only text files
ack
часто является удобным инструментом для рекурсивного -grep. Он по умолчанию ограничивает поиск текстовыми файлами (, определяемыми с помощью эвристики на основе имени файла и содержимого ), и по умолчанию пропускает такие каталоги, как .git
/ .svn
, что, если вы Разработчик, вероятно, то, что вы хотите.https://beyondgrep.com/.
Он входит в состав большинства дистрибутивов GNU/Linux, поэтому его легко установить. Он написан на Perl (, поэтому его регулярные выражения — это perl
регулярные выражения, аналогичные GNUgrep -P
).
ack -- "desired text" $(<subdirs.txt)
, вероятно, должен делать то, что вы хотите, и его очень легко набирать. Кроме того, он обеспечивает хороший цветной вывод для интерактивного использования.
(Различные способы разделения слова -subdirs.txt
на командную строку рассматриваются в других ответах. Возможно, вы захотите просто позволить стандартному разбиению слова оболочки -сделать это или readarray
разбить только на строки, а также заблокировать расширение глобуса.)