Za pomocą narzędzi GNU:
find . -type f -exec grep -lZ FIND {} + | xargs -r0 grep -l ME
Możesz zrobić standardowo:
find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;
Ale to uruchomiłoby dwa greps na plik. Aby uniknąć uruchamiania tylu grep
sekund i nadal być przenośnym, jednocześnie pozwalając na dowolny znak w nazwie pliku, możesz:
convert_to_xargs() {
sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
{
if (NR > 1) {
printf "%s", line
if (!index($0, "//")) printf "\\"
print ""
}
line = $0
}'
END { print line }'
}
find .//. -type f |
convert_to_xargs |
xargs grep -l FIND |
convert_to_xargs |
xargs grep -l ME
Chodzi o to, aby przekonwertować dane wyjściowe find
na format odpowiedni dla xargs (który oczekuje spacji (SPC / TAB / NL i innych spacji z twojego regionu z pewnymi implementacjami xargs
) oddzielonej listy słów, w których pojedyncze, podwójne cudzysłowy i ukośniki odwrotne mogą unikaj pustych miejsc i siebie nawzajem).
Zasadniczo nie można przetworzyć wyniku find -print
, ponieważ oddziela on nazwy plików znakiem nowej linii i nie zmienia znaków nowego wiersza znajdujących się w nazwach plików. Na przykład, jeśli zobaczymy:
./a
./b
Nie mamy sposobu, aby wiedzieć, czy jest to jeden plik wywoływany b
w katalogu o nazwie, a<NL>.
czy to dwa pliki a
i b
.
Używając .//.
, ponieważ //
nie może pojawić się inaczej w ścieżce pliku jako wyjście find
(ponieważ nie ma czegoś takiego jak katalog z pustą nazwą i /
nie jest dozwolony w nazwie pliku), wiemy, że jeśli widzimy wiersz zawierający //
, to jest to pierwszy wiersz nowej nazwy pliku. Możemy więc użyć tego awk
polecenia, aby uciec od wszystkich znaków nowego wiersza oprócz tych poprzedzających te wiersze.
Jeśli weźmiemy powyższy przykład, find
wynik byłby w pierwszym przypadku (jeden plik):
.//a
./b
Który awk ucieka do:
.//a\
./b
To xargs
postrzega to jako jeden argument. A w drugim przypadku (dwa pliki):
.//a
.//b
Co awk
by pozostało bez zmian, więc xargs
widzimy dwa argumenty.