Décomposer les arguments d'une ligne de commande (gère les guillemets)

Soyez le premier à donner votre avis sur cette source.

Vue 11 380 fois - Téléchargée 301 fois

Description

A partir d'une ligne de commande, cette fonction renvoie un tableau contenant la commande et ses arguments.

Le code gère l'emploi des guillemets. Ainsi, si votre commande est: shell "c:\program files\..."
l'argument renvoyé sera "c:\program files"
et non "c:\program".

Utilisation:
- si vous voulez traiter Command() quand votre appli se lance avec des arguments
- si vous voulez créer vos propres commandes dans une appli du style 'Console perso.'

J'espère que le code n'est pas trop embrouillé (la deuxiéme partie fait un peu usine à gaz); pour toute question et surtout pour toute amélioration, n'hésitez pas.

Source / Exemple :


Option Explicit

    ' Ce type de données contient les infos. relatives à l'occurrence des arguments
    Public Type TempType
        Pos1 As Integer     'Position de début dans strArgLine
        pos2 As Integer     'Position de fin dans strArgLine
        Filled As Boolean   'Si les champs Pos1 et Pos2 sont tous les deux renseignés
        Done As Boolean     'Quand l'argument a été passé en revue
    End Type

Sub main()

    ' Cette Sub ne sert qu'à tester la fonction ParseCommandLine

    Dim CmdLine As String
    Dim CommandLine() As String

    'C'est une ligne de commande de test
    CmdLine = "copy /Y c:\winnt\notepad.exe ""c:\program files"""

    'Appel de la fonction, qui renvoie un tableau
    CommandLine() = ParseCommandLine(CmdLine)
    
    'Ce tableau contient:
        '- en index 0, la commande
        '- en index > 0, les arguments
    Dim i As Integer
    For i = 0 To UBound(CommandLine())
        MsgBox CommandLine(i)
    Next i

End Sub

Public Function ParseCommandLine(strCommandLine As String) As String()
        
    Dim strArgs() As String         'Tableau temporaire contenant certains des arguments
    Dim CommandLine() As String     'Tableau temporaire contenant la commande et tous les arguments
    
    Dim strArgLine As String        'la chaîne contenant les arguments
    Dim strTemp As String           'Chaîne temporaire de traitement intermédiare
    
    Dim pos As Integer
    Dim pos2 As Integer
    
    Dim i As Integer
    Dim j As Integer
    Dim k As Integer
    

    '/ Traitement de strCommandLine
    
    strCommandLine = Trim(strCommandLine)       'On enlève les espaces
    If strCommandLine = "" Then Exit Function   'Si la ligne est vide, il n'y a rien à traiter
    
    ' On regarde s'il la commande est précédée d'arguments ou non
    pos = InStr(1, strCommandLine, " ")
    
    If pos = 0 Then
        'Il n'y a pas d'arguments
        ReDim CommandLine(0)                                'On dimensionne le tableau
        CommandLine(0) = strCommandLine                     'On le remplit à l'index 0 avec la commande
        strArgLine = ""                                     'Pas d'arguments à traiter
    Else
        ReDim CommandLine(0)                                'On dimensionne le tableau
        CommandLine(0) = Mid(strCommandLine, 1, pos - 1)    'On le remplit à l'index 0 avec la commande
        strArgLine = Mid(strCommandLine, pos + 1)           'On récupère les arguments à traiter
    End If

    '/ Traitement de la chaîne contenant les arguments (strArgLine)
    
    strArgLine = Trim(strArgLine)       'On enlève les espaces
    pos = InStr(1, strArgLine, """")    'On regarde si la chîne contient des guillemets
    
    If pos = 0 Then 'Pas de guillemets
        
        'Combien d'arguments ?
        pos2 = InStr(1, strArgLine, " ")
        
        If pos2 = 0 Then 'Un seul argument
            
            ReDim Preserve CommandLine(1)
            CommandLine(1) = strArgLine
        
        Else 'Plusieurs arguments
            
            'On utilise un tableau intermédiaire pour y stocker les arguments
            strArgs() = Split(strArgLine, " ")
            
            'On ajoute au tableau de résultat le contenu du tableau intermédiaire
            For i = 0 To UBound(strArgs())
                ReDim Preserve CommandLine(i)
                CommandLine(i) = strArgs(i)
            Next i
        
        End If
        
        
    Else 'Il y a des guillemets, c'est ici que ça se complique
        
        'Dans ce tableau, on stocke les mots séparés par des guillemets
        Dim intTemps As Integer
        Dim Temp() As TempType

        'On dimensionne le tableau
        intTemps = 0
        ReDim Temp(intTemps)
        
        'On définit le tableau comme étant vide à l'index concerné (0)
        Temp(intTemps).Filled = False
        
        
        ' /On remplit le tableau avec la position de début et de fin des chaînes contenues entre guillemets
        
        'On fixe la position de départ de la recherche des guillemets au 1er caractère de la chaîne des arguments
        pos = 1
        While pos <> 0
            'On recherche la position des guillemets
            pos = InStr(pos, strArgLine, """")
            If pos <> 0 Then
                
                'Si le tableau est complété à l'index concerné, on incrémente l'index et on redimensionne le tableau en conséquence
                If Temp(intTemps).Filled = True Then
                    intTemps = intTemps + 1
                    ReDim Preserve Temp(intTemps)
                    Temp(intTemps).Filled = False
                End If
                
                'Si la position de début n'a pas été stockée
                If Temp(intTemps).Pos1 = 0 Then
                    'On la stocke
                    Temp(intTemps).Pos1 = pos
                'Si la position de fin n'a pas été stockée
                ElseIf Temp(intTemps).pos2 = 0 Then
                    'On la stocke
                    Temp(intTemps).pos2 = pos
                    'On définit le tableau comme étant complété à l'index concerné (intTemps)
                    Temp(intTemps).Filled = True
                End If
            
                'On incrémente la position de départ de la recherche des guillemets, pour ne pas tourner en rond
                pos = pos + 1
            
            End If
        Wend
        
        
        'Dans ce tableau, on stocke les mots séparés par des espaces
        Dim Temp2a() As String
        Temp2a = Split(strArgLine, " ")
        
        'Dans ce tableau, on stocke la position des mots séparés par des espaces
        Dim intTemps2 As Integer
        Dim Temp2() As TempType
        intTemps2 = 0
        
        For i = 0 To UBound(Temp2a)
            intTemps2 = intTemps2 + 1
            ReDim Preserve Temp2(intTemps2)
            
            Temp2(intTemps2).Pos1 = Len(GetTable(Temp2a(), 0, i - 1)) + i * 1 + 1
            Temp2(intTemps2).pos2 = Temp2(intTemps2).Pos1 + Len(Temp2a(i)) - 1
        Next i
        
        
        'Maintenant, on ajoute au tableau final les différents éléments (élément séparés par des espaces et éléments situés entre guillemets), en supprimant les redondances, ie. les éléments séparés par des espaces ET situés entre guillemets
        
        Dim Found As Boolean
        
        For i = 1 To intTemps2
            
            'Par défaut, la chaîne séparée par des espaces ne correspond pas à une chaîne placée entre guillemets
            Found = False
            
            For j = 0 To intTemps
                
                If Temp2(i).Pos1 = Temp(j).Pos1 Then
                    'Si la chaîne ne correspond pas à une chaîne placée entre guillemets
                    Found = True
                    
                    'On ajoute la chaîne entre guillemets au tableau final des arguments
                    ReDim Preserve CommandLine(UBound(CommandLine()) + 1)
                    CommandLine(UBound(CommandLine())) = Mid(strArgLine, Temp(j).Pos1, Temp(j).pos2 - Temp(j).Pos1 + 1)
                    
                    'On définit qu'elle a été traitée
                    Temp2(i).Done = True
                    
                    'Pour les chaînes séparées par des espaces qui appartiennent à la chaîne placée entre guillemets,
                    'on définit qu'elles ont été traitées.
                    For k = i To intTemps2
                        If Temp2(k).pos2 <= Temp(j).pos2 Then
                            Temp2(k).Done = True
                        End If
                    Next k
                    
                End If
            Next j
            
            If Found = False Then 'La chaîne ne correspond pas à une chaîne placée entre guillemets
                
                If Temp2(i).Done = False Then 'Sil la chaîne n'a pas été traitée auparavant
                    
                    'On l'ajoute au tableau final des arguments
                    ReDim Preserve CommandLine(UBound(CommandLine()) + 1)
                    CommandLine(UBound(CommandLine())) = Mid(strArgLine, Temp2(i).Pos1, Temp2(i).pos2 - Temp2(i).Pos1 + 1)
                    
                    'On définit qu'elle a été traitée
                    Temp2(i).Done = True
                End If
            
            End If
            
        Next i
        
    End If
    
    'On renvoie le résultat de la fonction
    ParseCommandLine = CommandLine()

End Function

' Cette function rassemble en une chaîne les éléments d'un tableau (sans y mettre les séparateurs), d'un index de début à un index de fin.
Function GetTable(strTable() As String, IndexFrom As Integer, IndexTo As Integer)

    Dim i As Integer
    Dim strTemp As String
    
    For i = IndexFrom To IndexTo
        strTemp = strTemp & strTable(i)
    Next i

    GetTable = strTemp
End Function

Codes Sources

A voir également

Ajouter un commentaire Commentaires
Messages postés
17286
Date d'inscription
mercredi 2 janvier 2002
Statut
Modérateur
Dernière intervention
23 décembre 2019
69
sans vouloir relancer la polémique...

voici un code fonctionnel (on m'a demandé ce code, je voulais filer une version claire...)


Private Sub Form_Load()
Dim i As Long
Dim xsParts() As String
xsParts = ParseCommandLine("")
For i = LBound(xsParts) To UBound(xsParts)
Debug.Print i, xsParts(i)
Next i
Unload Me
End Sub

Private Function ParseCommandLine(Optional ByVal vsCommandLine) As String()
Dim i As Long
Dim bInQuotes As Boolean
Dim nChar As Integer
'# Si aucune chaine avec les arguments de ligne de commande n'a été fournie...
If IsMissing(vsCommandLine) Then
'# On utilise celle recue par l'executable, obtenu grâce a la fonction Command$()
vsCommandLine = Trim$(Command$())
Else
'# sinon, on utilise celle effectivement fournie par l'utilisateur.
vsCommandLine = Trim$(vsCommandLine)
End If
If LenB(vsCommandLine) Then
'# On regarde la chaine, caractère par caractère
For i = 1 To Len(vsCommandLine)
'# On évite d'extraire deux fois le même caractère
nChar = AscW(Mid$(vsCommandLine, i, 1))
'# Si le caractère considéré est une guillemet, on incrémente le compteur
If nChar = 34 Then
bInQuotes = Not bInQuotes
'# si c'est un espace...
ElseIf nChar = 32 Then
'# ...et que nous sommes en dehors d'un 'tronçon' avec des guillemets,
'# on remplace l'espace par un carctère \0
If Not bInQuotes Then
Mid$(vsCommandLine, i, 1) = vbNullChar
End If
End If
Next i

'# tout tronçon de chaîne ouvert par des " doit être fermé
'# On teste donc si le nombre de guillemets est pair ou impair
If bInQuotes Then
Err.Raise vbString, , "Construction de la chaîne de paramètres incorrecte"
Else
'# On supprime les guillemets de la chaine finale
vsCommandLine = Replace(vsCommandLine, ChrW$(34), vbNullString)
'# On découpe la ligne de commande à chaque \0 rencontré
ParseCommandLine = Split(vsCommandLine, vbNullChar)
End If
Else
ReDim ParseCommandLine(0) As String
End If
End Function
Messages postés
78
Date d'inscription
samedi 16 mars 2002
Statut
Membre
Dernière intervention
7 septembre 2006

Si il y a aucune quotes, à la fin de la boucle de vérification, bQuotes est true, et donc pas de messages d'erreur.
J'ai testé la fonction avec plusieurs cas de figure, cela marche parfaitement.
Messages postés
13280
Date d'inscription
lundi 13 décembre 2004
Statut
Modérateur
Dernière intervention
3 février 2018
43
tu proposes d'initialiser à true. mais s'il n'y a aucune quote????
++
Messages postés
78
Date d'inscription
samedi 16 mars 2002
Statut
Membre
Dernière intervention
7 septembre 2006

La seule chose qui importe est de savoir si on a un nombre pair (correct) ou impair (incorrect) de quotes. A la fin de la boucle, si bQuotes est false, c'est qu'il y a un nombre impair de quotes ==> message d'erreur.
Quand à la simplicité du code, elle me parrait évidente : qu'est-ce qui est plus lisible : "if cBool(nQuotes and 1) Then..." ou "if not bQuotes Then..." ?
Messages postés
13280
Date d'inscription
lundi 13 décembre 2004
Statut
Modérateur
Dernière intervention
3 février 2018
43
clafouti -> et sorti de ta boucle tu vois comment qu'il y en avait ou pas (des ")? ....
et si cette proposition était valable (mais c'est pas le cas), je ne vois pas en quoi soudainement le code devient "plus simple" ;)
si?
Afficher les 45 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.