/ / Optymalny sposób rekurencyjnego znajdowania plików pasujących do jednego lub więcej wzorców - bash, grep, find

Optymalny sposób rekurencyjnego znajdowania plików pasujących do jednego lub więcej wzorców - bash, grep, find

Muszę zoptymalizować skrypt powłoki, ale po tygodniu nie udało mi się go zoptymalizować.

Muszę wyszukać rekurencyjnie dla plików .c .h i .cpp w katalogu i sprawdzić, czy istnieje takie słowo: "float short unsigned continue for signed void default goto sizeof volatile do if static while"

words=$(echo $@ | sed "s/ /\|/g")

files=$(find $dir -name "*.cpp" -o -name "*.c" -o -name "*.h" )

for file in  $files; do
(
test=$(grep -woh "$words" "$file" | sort -u | awk "{print}" ORS=" ")
if [ "$test" != "" ] ; then
echo "$(realpath $file) contains : $test"
fi
)&
done
wait

Próbowałem z xargs i -exec, ale bez rezultatu, muszę zachować ten format wyniku:

/usr/include/c++/6/bits/stl_set.h zawiera: domyślnie dla void

Może możesz mi pomóc (zoptymalizować to) ..

EDYCJA: Muszę zachować jedno wystąpienie każdego słowa TAK: podczas gdy dla, niestabilny ... NOPE: podczas, na, na, lotny ...

Odpowiedzi:

0 dla odpowiedzi № 1

Jeśli jesteś zainteresowany znalezieniem wszystkich plików, które mają co najmniej jeden odpowiednik dowolnego z twoich wzorców, możesz użyć globstar:

shopt -s globstar
oldIFS=$IFS; IFS="|"; patterns="$*"; IFS=$oldIFS  # make a | delimited string from arguments
grep -lwE "$patterns" **/*.c **/*.h **/*.cpp       # list files with matching patterns

globstar

Jeśli jest ustawiony, wzorzec "**" używany w nazwie plikukontekst rozszerzenia dopasuje wszystkie pliki i zero lub więcej katalogów i podkatalogów. Jeśli po wzorcu występuje "/", tylko katalogi i podkatalogi pasują.


0 dla odpowiedzi nr 2

Oto podejście, które utrzymuje pożądany format, eliminując użycie pętli find i bash:

words="float|short|unsigned|continue|for|signed|void|default|goto|sizeof|volatile|do|if|static|while"
grep  -rwoE --include "*.[ch]" --include "*.cpp" "$words" path | awk -F: "$1!=last{printf "%s%s: contains %s",r,$1,$2; last=$1; r=ORS; delete a; a[$2]} $1==last && !($2 in a){printf " %s",$2; a[$2]} END{print""}"

Jak to działa

  • grep -rwoE --include "*.[ch]" --include "*.cpp" "$words" ścieżka

    Przeszukuje rekursywnie przez katalogi zaczynające się od path szukanie tylko w plikach, których nazwy pasują do globów *.[ch] lub *.cpp.

  • awk -F: "$1!=last{printf "%s%s: contains %s",r,$1,$2; last=$1; r=ORS; delete a; a[$2]} $1==last{printf " %s",$2} END{print""}"

    To polecenie awk zmienia format wyjściowy grep aby dopasować pożądany wynik. Skrypt używa zmiennej last i tablica a. last śledzi, który plik mamy i a zawiera listę słów dotychczasowych. Bardziej szczegółowo:

    • -F:

      To mówi awk, aby użyć : jako separator pól. W ten sposób pierwsze pole jest nazwą pliku, a drugie jest znalezionym słowem. (ograniczenie: nazwy plików, które zawierają : nie są obsługiwane.)

    • "$ 1! = Last {printf"% s% s: zawiera% s ", r, 1 $, 2 $; last = $ 1; r = ORS; usuń a; a [$ 2]}

      Za każdym razem, gdy nazwa pliku, $1, nie pasuje do zmiennej last, zaczynamy wyjście dla nowego pliku. Następnie aktualizujemy last zawierać nazwę tego nowego pliku. Następnie usuwamy tablicę a a następnie przypisz klucz $2 do nowej tablicy a.

    • $1==last && !($2 in a){printf " %s",$2; a[$2]}

      Jeśli obecna nazwa pliku jest taka sama jak poprzednia, a bieżące słowo nie było wcześniej widoczne, wydrukujemy nowe znalezione słowo. Dodajemy również to słowo, $2 jako klucz do tablicy a.

    • END{print""}

      Spowoduje to wydrukowanie ostatecznego znaku nowego wiersza (separatora rekordów).

Wersja wielowierszowa kodu

Dla tych, którzy wolą swój kod rozłożony na wiele linii:

grep  -rwoE 
--include "*.[ch]" 
--include "*.cpp" 
"$words" path |
awk -F: "
$1!=last{
printf "%s%s: contains %s",r,$1,$2
last=$1
r=ORS
delete a
a[$2]
}
$1==last && !($2 in a){
printf " %s",$2; a[$2]
}
END{
print""
}"

0 dla odpowiedzi № 3

Powinieneś być w stanie zrobić większość tego z jednym grep dowództwo:

grep -Rw $dir --include *.c --include *.h --include *.cpp -oe "$words"

Spowoduje to zapisanie pliku: word format, więc wszystko, co pozostało, to zmienić to, aby uzyskać pożądany wynik.

echo $output | sed "s/:/ /g" | awk "{print $1 " contains : " $2}"

Następnie możesz dodać | sort -u aby uzyskać tylko pojedyncze wystąpienia dla każdego słowa w każdym pliku.


#!/bin/bash

#dir=.
words=$(echo $@ | sed "s/ /\|/g")

grep -Rw $dir --include *.c --include *.h --include *.cpp -oe "$words" 
| sort -u 
| sed "s/:/ /g" 
| awk "{print $1 " contains : " $2}"