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 grepsekund 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 findna 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 bw katalogu o nazwie, a<NL>.czy to dwa pliki ai 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 awkpolecenia, aby uciec od wszystkich znaków nowego wiersza oprócz tych poprzedzających te wiersze.
Jeśli weźmiemy powyższy przykład, findwynik byłby w pierwszym przypadku (jeden plik):
.//a
./b
Który awk ucieka do:
.//a\
./b
To xargspostrzega to jako jeden argument. A w drugim przypadku (dwa pliki):
.//a
.//b
Co awkby pozostało bez zmian, więc xargswidzimy dwa argumenty.