Ich muss ein Shell-Skript optimieren, aber nach einer Woche ist es mir nicht gelungen, es ausreichend zu optimieren.
Ich muss rekursiv nach .c .h und .cpp-Dateien in einem Verzeichnis suchen und prüfen, ob ein solches Wort existiert: "float short unsigned fortfahren für signed void default goto sizeof volatile tun wenn statisch während"
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
Ich habe versucht mit xargs und -exec, aber kein Ergebnis, ich muss dieses Ergebnisformat behalten:
/usr/include/c++/6/bits/stl_set.h enthält: Standard für wenn ungültig
Vielleicht kannst du mir helfen (um es zu optimieren) ..
EDIT: Ich muss ein Vorkommen von jedem Wort halten JA: während, für, volatile ... NOPE: während, für, für flüchtige ...
Antworten:
0 für die Antwort № 1Wenn Sie daran interessiert sind, alle Dateien zu finden, die mindestens eine Übereinstimmung mit einem Ihrer Muster haben, können Sie verwenden 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
Wenn gesetzt, wird das Muster '**' in einem Dateinamen verwendetErweiterungskontext passt alle Dateien und null oder mehr Verzeichnisse und Unterverzeichnisse an. Wenn auf das Muster ein '/' folgt, nur Verzeichnisse und Unterverzeichnisse stimmen überein.
0 für die Antwort № 2
Hier ist ein Ansatz, der das gewünschte Format beibehält und die Verwendung von find und bash looping überflüssig macht:
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""}"
Wie es funktioniert
grep -rwoE --include "*.[ch]" --include "*.cpp" "$words"
PfadDies führt rekursiv durch Verzeichnisse, die mit beginnen
path
nur in Dateien suchen, deren Namen mit den Globs übereinstimmen*.[ch]
oder*.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""}"
Dieser awk Befehl formatiert die Ausgabe von
grep
passend zu Ihrer gewünschten Ausgabe. Das Skript verwendet eine Variablelast
und Arraya
.last
verfolgt, auf welcher Datei wir sind unda
enthält die Liste der bisher gesehenen Wörter. Ausführlicher:-F:
Dies sagt awk zu verwenden
:
als Feldtrennzeichen. Auf diese Weise ist das erste Feld der Dateiname und das zweite ist das gefundene Wort. (Einschränkung: Dateinamen, die einschließen:
werden nicht unterstützt.)"$ 1! = Letzte {printf"% s% s: enthält% s ", r, $ 1, $ 2; last = $ 1; r = ORS; löschen a; a [$ 2]}
Jedes Mal, wenn der Dateiname
$1
, stimmt nicht mit der Variablen übereinlast
Wir starten die Ausgabe für eine neue Datei. Dann aktualisieren wirlast
um den Namen dieser neuen Datei zu enthalten. Wir löschen dann das Arraya
und dann Schlüssel zuweisen$2
zu einem neuen Arraya
.$1==last && !($2 in a){printf " %s",$2; a[$2]}
Wenn der aktuelle Dateiname derselbe wie der vorherige ist und das aktuelle Wort vorher nicht gesehen wurde, drucken wir das neue gefundene Wort aus. Wir fügen auch dieses Wort hinzu,
$2
als Schlüssel zum Arraya
.END{print""}
Dadurch wird ein endgültiges Zeilenumbruchzeichen (Datensatztrennzeichen) gedruckt.
Mehrzeilige Version des Codes
Für diejenigen, die ihren Code über mehrere Zeilen verteilt bevorzugen:
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 für die Antwort № 3
Sie sollten das meiste davon mit einem einzigen tun können grep
Befehl:
grep -Rw $dir --include *.c --include *.h --include *.cpp -oe "$words"
Dies wird es in das Datei: Word-Format bringen, so dass es nur übrig bleibt, es zu ändern, um die gewünschte Ausgabe zu erzeugen.
echo $output | sed "s/:/ /g" | awk "{print $1 " contains : " $2}"
Dann können Sie hinzufügen | sort -u
um nur einzelne Vorkommnisse für jedes Wort in jeder Datei zu erhalten.
#!/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}"