- Offizieller Beitrag
Jeder wird es kennen, ein Skript wächst und wächst. Variablen werden deklariert und manchmal auch entfernt. Und zum Schluß weiß man gar nicht, ob alle deklarierten auch verwendet werden.
Mit diesem Skript könnt ihr:
- einmalig genutze Variablen (Standard) oder
- alle verwendeten Variablen ausgeben lassen
Die Ausgabe enthält:
- Funktionsbereich (Funktionsname für Variablen in Funktionen, sonst 'Out_Of_Func')
- Variablenname
- Anzahl Vorkommen
- Zeile(n) des Vorkommens
Die Ausgabe erfolgt als:
- String in die Konsole (Standard)
- Array
Ignoriert werden Variablen in Kommentarzeilen/-blöcken und ebenso Zählervariablen in For-Schleifen.
Ich prüfe (noch) nicht gegen includierte Konstanten, sodass diese bei einmaliger Verwendung auch als 'ungenutzt' erscheinen. Diese Prüfung werde ich später noch einbauen. Im Moment testet mal, ob es bei diversen Skripten auch funktioniert.
Es gibt eine bekannte Konstellation, in der ein Kommentar nicht erkannt wird:
Bsp:
$sVariable = 'irgendwas;' ; Kommentar dazu und dieselben 'Stringbegrenzer', wie in der Zuweisung, $sVariable enthält beliebigen String
[/autoit]In dieser Zeile kann der Kommentar nicht lokalisiert werden, da das letzte Semikolon als Stringbestandteil interpretiert wird: ' ; Kommentar dazu und dieselben ' und somit der Variablenname im Kommentar mitgezählt wird. Da dieser Fall sicher sehr unwahrscheinlich ist sehe ich das aber als unkritisch an.
Die Array-Ausgabe sieht z.B. so aus
Am einfachsten die Funktion in das SciTE-Menü einbinden "SciTEUser.properties"
# END => DO NOT CHANGE ANYTHING BEFORE THIS LINE #-#-#-#-#-#
# 39 Unused Vars
command.39.*.au3="$(autoit3dir)\autoit3.exe" "$(SciteDefaultHome)\GetUnusedVars.au3" "$(FilePath)" "1" "0"
command.name.39.*.au3=GetUnusedVars
command.save.before.39.*.au3=1
command.is.filter.39.*.au3=1
command.shortcut.39.*.au3=Ctrl+Shift+Alt+U
Den Shortcut könnt ihr natürlich anpassen, ebenso, wie den Pfad zur Datei.
Die Aufrufzeile enthält zwei optionale Parameter, hier: "1" "0"
Diese stehen für
Alle-Variablen: "0" (Standard, einmalig) oder "1" (alle Vorkommen) und
Ausgabe: "1" (Standard, in Konsole) oder "0" (als Array)
Edit: Kleiner 'Guttenberg-Error' (gefixt)
Hier der Code:
GetUnusedVars.au3
#include-once
#include <File.au3>
#include <Array.au3>
If $CmdLine[0] Then
Local $sFile = $CmdLine[1], $fAll = 0, $fString = 1
Switch $CmdLine[0]
Case 3
$fAll = $CmdLine[2]
$fString = $CmdLine[3]
Case 2
$fAll = $CmdLine[2]
EndSwitch
Else
Exit
EndIf
ConsoleWrite('+>' & @HOUR & ':' & @MIN & ':' & @SEC & ' Start: Get_Unused_Vars' & @CRLF)
Local $vReturn = _GetUnusedVarsConst($sFile, $fAll, $fString)
Local $error = @error
ConsoleWrite('+>' & @HOUR & ':' & @MIN & ':' & @SEC & ' Ende: Get_Unused_Vars' & @CRLF)
If @error Then ConsoleWrite('!Fehler: Datei "' & $sFile & ' existiert nicht.' & @CRLF)
If $fString = 0 Then
_ArrayDisplay($vReturn)
Else
ConsoleWrite($vReturn & @CRLF)
EndIf
;===============================================================================
; Function Name: _GetUnusedVarsConst
; Description: Ermittelt ungenutzte Variablen/Konstanten in einem Skript
; Optional können alle Variablen mit Anzahl der Vorkommen ermittelt werden
; Ignoriert werden:
; Variablen in Kommentarzeilen
; Variablen in Kommentarblöcken
; Zählervariablen (For-Schleife)
; Parameter(s): $sFile Pfad zur Skriptdatei
; $fAll 1 - Rückgabe aller Variablen; 0(Standard) - einmalige Variablen
; $fString 1(Standard) - Rückgabe in Konsole; 0 - Anzeige Array
; Return Value(s): Erfolg Standard Array/String mit nur einmalig auftauchender Variable
; Optional Array/String mit allen Variablen
; Ausgabe: Funktionsbereich - Variable - Anzahl - Position:Zeile(n)
; Fehler 0 @error = 1 $sFile nicht vorhanden
; Author(s): BugFix ([email='bugfix@autoit.de'][/email])
;===============================================================================
Func _GetUnusedVarsConst($sFile, $fAll=0, $fString=1)
Local $oVars = ObjCreate('Scripting.Dictionary')
Local $oFuncVars = ObjCreate('Scripting.Dictionary')
Local $aFuncs[1][2] = [[0]] ; [['Funktionsname','var1/Anz1/Zeile(1.),Zeile(2.)..' & '|' & 'var1/Anz1/Zeile(1.),Zeile(2.)..' & '|' & 'var(n)/Anz(n)/Zeile(1.),Zeile(2.)..' ]]
Local $aFile, $tmp, $aSplit, $aMatch, $inFunc = False, $counterFor = '', $1stChar, $iPos, $sSign, $fSet, $iSemicolon, $skip, $sFuncName
If Not FileExists($sFile) Then Return SetError(1,0,0)
_FileReadToArray($sFile, $aFile)
For $i = 1 To $aFile[0]
; Kommentarblöcke werden übersprungen
; auskommentierte Zeilen werden übersprungen
$1stChar = StringLeft(StringStripWS($aFile[$i],1), 1)
If $1stChar = ';' Then ContinueLoop
If (StringInStr($aFile[$i], '#cs') Or StringInStr($aFile[$i], '#comments-start')) Then $skip = True
If (StringInStr($aFile[$i], '#ce') Or StringInStr($aFile[$i], '#comments-end')) Then $skip = False
If $skip Then ContinueLoop
Select
Case StringLeft(StringStripWS($aFile[$i], 1), 4) = 'Func'
; Beginn Funktionscode
; Funktionsnamen auslesen u. in Func-Array eintragen
; Funktionszähler +1
$inFunc = True
$aMatch = StringRegExp($aFile[$i], '^(?:[F|f]unc\s+)([_a-zA-Z0-9]+)', 1)
$sFuncName = $aMatch[0]
; Variablen aus Funktionsheader (auch mehrzeilig) auslesen und in $oFuncVars erfassen
; steht eine Variable in einem Kommentar hinter dem Header wird sie ignoriert
$aMatch = StringRegExp($aFile[$i], '(?:[F|f]unc\s+[\d\w]+\()[$,\w\s]*(?:\)?)', 1) ; Codeteil vor mgl. Kommentar oder '_'
$aMatch = StringRegExp($aMatch[0], '(\$[\d\w]+)', 3)
If IsArray($aMatch) Then __VarObjectAdd($oFuncVars, $aMatch, $counterFor, $i)
Case StringLeft(StringStripWS($aFile[$i], 1), 7) = 'EndFunc'
; Ende Funktionscode
; in $oFuncVars gesammelte Variablen/Anzahl in $aFuncs übertragen
; optional ($fAll=True) werden alle Variablen mit Anzahl Vorkommen übertragen
; standardmäßig nur Variablen mit einmaligem Vorkommen
If $oFuncVars.Count Then
__SetToFuncArray($aFuncs, $oFuncVars, $fAll, $sFuncName)
$oFuncVars.RemoveAll
EndIf
$inFunc = False
Case Else ; Codezeilen innerhalb oder außerhalb einer Funktion, durch $inFunc markiert
; prüfen auf For-Schleife, Selektion Zählervariable
If StringLeft(StringStripWS($aFile[$i], 1), 3) = 'For' Then
$aMatch = StringRegExp($aFile[$i], '(?:[F|f]or\s+)(\$[\d\w]+)', 1)
$counterFor = $aMatch[0]
EndIf
; prüfen auf Ende For-Schleife, Zurücksetzen Zählervariable, nächste Zeile
If StringLeft(StringStripWS($aFile[$i], 1), 4) = 'Next' Then
$counterFor = ''
ContinueLoop
EndIf
; prüfen ob ';' enthalten und wieviel
StringReplace($aFile[$i], ';', '')
$iSemicolon = @extended
$fSet = False
If $iSemicolon Then
; Anzahl der ';' gibt keine Auskunft ob Kommentar od. Wertzuweisung
; wenn Wertzuweisung, muß vor und nach dem ; ein ' oder " sein (Stringbegrenzer)
; ist eines der Zeichen enthalten könnte es eine Wertzuweisung sein
; es braucht nur das letzte Vorkommen geprüft werden, wenn nicht in Stringbegrenzern ist es Kommentar
; wenn Kommentar - diesen Teil abtrennen
; ABER: wenn letzter Stringbegrenzer vor Semikolon == mit einem Stringbegrenzer im Kommentar ==> Kommentar wird nicht erkannt!
If StringInStr($aFile[$i], '"') Or StringInStr($aFile[$i], "'") Then ; sind Stringbegrenzer vorhanden
$sSign = ''
$iPos = StringInStr($aFile[$i], ';', 1, -1) ; letztes Vorkommen ermitteln
$aSplit = StringSplit($aFile[$i], '')
For $j = $iPos -1 To 1 Step -1 ; rückwärts iterieren ab Semikolon
If $aSplit[$j] = '"' Or $aSplit[$j] = "'" Then
$sSign = $aSplit[$j]
ExitLoop
EndIf
Next
; wenn Stringbegrenzer links gefunden
If $sSign <> '' Then ; vorwärts iterieren ab Semikolon
For $j = $iPos +1 To $aSplit[0]
If $aSplit[$j] = $sSign Then ; davor und danach dasselbe ' od. " ==> also Zuweisung
$fSet = True
ExitLoop
EndIf
Next
EndIf
EndIf
If Not $fSet Then ; ist Kommentar, nicht Zuweisung
$aFile[$i] = StringLeft($aFile[$i], StringInStr($aFile[$i], ';', 1, -1) -1)
EndIf
EndIf
; Variablen selektieren und an $oFuncVars oder $oVars übergeben
$aMatch = StringRegExp($aFile[$i], '(\$[\d\w]+)', 3)
If IsArray($aMatch) Then
If $inFunc = True Then
__VarObjectAdd($oFuncVars, $aMatch, $counterFor, $i)
Else
__VarObjectAdd($oVars, $aMatch, $counterFor, $i)
EndIf
EndIf
EndSelect
Next
If $oVars.Count Then __SetToFuncArray($aFuncs, $oVars, $fAll)
Local $aRet[1][4] = [['Funktionsbereich','Variable','Anzahl','Zeile(n)']], $aSplit2, $sRet = 'Funktionsbereich - Variable - Anzahl - Zeile(n)' & @CRLF
For $i = 1 To UBound($aFuncs) -1
$aSplit = StringSplit($aFuncs[$i][1], '|')
For $j = 1 To $aSplit[0]
$aSplit2 = StringSplit($aSplit[$j], '/')
If @error Then ContinueLoop
$sRet &= $aFuncs[$i][0] & ' - ' & $aSplit2[1] & ' - ' & $aSplit2[2] & ' - ' & $aSplit2[3] & @CRLF
If $fString = 0 Then
ReDim $aRet[UBound($aRet)+1][4]
$aRet[UBound($aRet)-1][0] = $aFuncs[$i][0]
$aRet[UBound($aRet)-1][1] = $aSplit2[1]
$aRet[UBound($aRet)-1][2] = $aSplit2[2]
$aRet[UBound($aRet)-1][3] = $aSplit2[3]
EndIf
Next
Next
If $fString = 0 Then Return $aRet
Return $sRet
EndFunc ;==>_GetUnusedVarsConst
Func __VarObjectAdd(ByRef $oDict, $aMatch, $counterFor, $line)
Local $aSplit
For $i = 0 To UBound($aMatch) -1
If $aMatch[$i] = $counterFor Then ContinueLoop ; Zählervariablen werden nicht gezählt
If $oDict.Exists($aMatch[$i]) Then
$aSplit = StringSplit($oDict.Item($aMatch[$i]), '/')
$oDict.Item($aMatch[$i]) = $aSplit[1] +1 & '/' & $aSplit[2] & ',' & $line
Else
$oDict.Add($aMatch[$i], 1 & '/' & $line)
EndIf
Next
EndFunc ;==>__VarObjectAdd
Func __SetToFuncArray(ByRef $aFuncs, ByRef $oDict, $fAll, $sFuncName='')
Local $aSplit, $colKeys, $sVarCount = ''
$colKeys = $oDict.Keys
For $key In $colKeys
If $sFuncName <> '' Then
ReDim $aFuncs[UBound($aFuncs)+1][2]
$aFuncs[UBound($aFuncs)-1][0] = $sFuncName
EndIf
If $fAll = 0 Then
If Not StringRegExp($oDict.Item($key), '(1/\d+)') Then ContinueLoop
EndIf
$aSplit = StringSplit($oDict.Item($key), '/')
$sVarCount &= $key & '/' & $aSplit[1] & '/' & $aSplit[2] & '|'
If $sFuncName <> '' Then
$aFuncs[UBound($aFuncs)-1][1] = StringTrimRight($sVarCount, 1)
$sVarCount = ''
EndIf
Next
If $sFuncName = '' Then
ReDim $aFuncs[UBound($aFuncs)+1][2]
$aFuncs[UBound($aFuncs)-1][0] = 'Out_Of_Func'
$aFuncs[UBound($aFuncs)-1][1] = StringTrimRight($sVarCount, 1)
EndIf
EndFunc ;==>__SetToFuncArray
ToDoList:
- auf includierte Konstanten prüfen
- auf includierte Globale Variablen prüfen
- Globale Variablen gegen Variablen in Funktionen abgleichen