Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

  1. #1
    Chroniqueur Actualités

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    mars 2013
    Messages
    4 738
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Administrateur de base de données

    Informations forums :
    Inscription : mars 2013
    Messages : 4 738
    Points : 123 313
    Points
    123 313
    Par défaut Avec PowerShell et WSL, intégrez des commandes Linux à Windows comme s'il s'agissait d'apps natives
    « Avec PowerShell et WSL, nous pouvons intégrer des commandes Linux à Windows comme s'il s'agissait d'applications natives »,
    selon un ingénieur Microsoft

    Avec WSL 2, Microsoft a apporté une nouvelle version de l’architecture qui permet au sous-système Windows pour Linux d’exécuter des fichiers binaires ELF64 Linux sous Windows. Cette nouvelle architecture, qui utilise un véritable noyau Linux, modifie la façon dont ces binaires Linux interagissent avec Windows et le matériel de votre ordinateur, tout en offrant la même expérience utilisateur que dans WSL 1 (la version actuellement disponible en version stable). WSL 2 offre des performances de système de fichiers beaucoup plus rapides et une compatibilité totale des appels système, ce qui vous permet d'exécuter davantage d'applications comme Docker!

    WSL 2 inclut donc un véritable noyau Linux qui vous permet d’exécuter davantage de logiciels Linux sous Windows et offrant de meilleures performances que WSL 1.

    Cette nouvelle version de WSL 2 utilise les fonctionnalités Hyper-V pour créer une machine virtuelle légère avec un noyau Linux minimal. Il faut s’attendre à une meilleure compatibilité avec les logiciels Linux, y compris la prise en charge de Docker, et « une augmentation spectaculaire des performances du système de fichiers ».

    Mais Microsoft a voulu expliquer aux utilisateurs qu'ils pouvaient aller plus loin. Dans un billet de blog, Mike Battista, Senior Program Manager Windows Developer Platform, avance que :

    « Une question récurrente chez les développeurs Windows est la suivante : "Pourquoi Windows n’a-t-il pas encore <INSERT FAVORITE LINUX COMMAND HERE>?". Qu'ils aspirent à utiliser un puissant pageur comme less ou à utiliser des commandes familières telles que grep ou sed, les développeurs Windows souhaitent pouvoir accéder facilement à ces commandes dans le cadre de leur flux de travail principal » .

    Il a noté que le sous-système Windows pour Linux (WSL) constituait un énorme pas en avant en permettant aux développeurs d’appeler des commandes Linux à partir de Windows en les mettant en proxy par le biais de wsl.exe (par exemple, wsl ls). Cependant, il a relevé les limites de cette expérience à plusieurs niveaux, notamment :
    • Préfixer les commandes avec wsl est fastidieux et peu naturel
    • Les chemins Windows transmis en tant qu'arguments ne sont pas souvent résolus, car les barres obliques inverses sont interprétées comme des caractères d'échappement plutôt que comme des séparateurs de répertoire ;
    • Les chemins Windows transmis en tant qu'arguments ne sont pas souvent résolus, car ils ne sont pas traduits vers le point de montage approprié dans WSL ;
    • Les paramètres par défaut définis dans les profils de connexion WSL avec des alias et des variables d’environnement ne sont pas respectés ;
    • La complétion de chemin Linux n'est pas supportée ;
    • La complétion de la commande n'est pas supportée ;
    • La complétion de l'argument n'est pas prise en charge.

    « Le résultat de ces lacunes est que les commandes Linux se sentent comme des citoyens de deuxième classe vis-à-vis de Windows et sont plus difficiles à utiliser qu’elles ne devraient l’être. Pour qu'une commande ressemble à une commande Windows native, nous devons résoudre ces problèmes ».

    Nom : wsl.png
Affichages : 16985
Taille : 126,7 Ko

    Encapsuleurs de fonctions PowerShell

    Mike affirme que nous pouvons supprimer le besoin de préfixer les commandes par wsl, gérer la traduction des chemins Windows en chemins WSL et prendre en charge la complétion des commandes avec les wrappers de fonctions PowerShell. Les exigences de base des wrapper sont les suivantes :
    • Il devrait y avoir un wrapper de fonction par commande Linux portant le même nom que la commande ;
    • Le wrapper doit reconnaître les chemins Windows transmis en tant qu'arguments et les traduire en chemins WSL ;
    • Le wrapper doit appeler wsl avec la commande Linux correspondante, en canalisant toute entrée de pipeline et en transmettant tous les arguments de ligne de commande passés à la fonction.

    Ce modèle pouvant être appliqué à n’importe quelle commande, nous pouvons résumer la définition de ces wrappers et les générer de manière dynamique à partir d’une liste de commandes à importer.

    Code PowerShell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # The commands to import.
    $commands = "awk", "emacs", "grep", "head", "less", "ls", "man", "sed", "seq", "ssh", "tail", "vim"
     
    # Register a function for each command.
    $commands | ForEach-Object { Invoke-Expression @"
    Remove-Alias&nbsp;$_ -Force -ErrorAction Ignore
    function global:$_() {
        for (`$i = 0; `$i -lt `$args.Count; `$i++) {
            # If a path is absolute with a qualifier (e.g. C:), run it through wslpath to map it to the appropriate mount point.
            if (Split-Path `$args[`$i] -IsAbsolute -ErrorAction Ignore) {
                `$args[`$i] = Format-WslArgument (wsl.exe wslpath (`$args[`$i] -replace "\\", "/"))
            # If a path is relative, the current working directory will be translated to an appropriate mount point, so just format it.
            } elseif (Test-Path `$args[`$i] -ErrorAction Ignore) {
                `$args[`$i] = Format-WslArgument (`$args[`$i] -replace "\\", "/")
            }
        }
     
        if (`$input.MoveNext()) {
            `$input.Reset()
            `$input | wsl.exe&nbsp;$_ (`$args -split ' ')
        } else {
            wsl.exe&nbsp;$_ (`$args -split ' ')
        }
    }
    "@
    }

    La liste $command définit les commandes à importer. Ensuite, nous générons dynamiquement l'encapsuleur de fonction pour chacune à l'aide de la commande Invoke-Expression (en supprimant tout d'abord les alias susceptibles d'entrer en conflit avec la fonction).

    La fonction parcourt les arguments de ligne de commande, identifie les chemins Windows à l'aide des commandes Split-Path et Test-Path, puis convertit ces chemins en chemins WSL. Nous exécutons les chemins via une fonction d'assistance que nous définirons plus tard, appelée Format-WslArgument, qui permet d'échapper les caractères spéciaux tels que les espaces et les parenthèses qui seraient autrement mal interprétés.

    Enfin, nous transmettons les entrées de pipeline et tous les arguments de ligne de commande à wsl.

    Avec ces wrappers de fonctions en place, nous pouvons maintenant appeler nos commandes Linux préférées de manière plus naturelle sans avoir à les préfixer avec wsl ni à nous préoccuper de la façon dont les chemins Windows sont traduits en chemins WSL:
    • man bash
    • less -i $profile.CurrentUserAllHosts
    • ls -Al C:\Windows\ | less
    • grep -Ein error *.log
    • tail -f *.log

    Un ensemble de commandes est présenté ici, mais vous pouvez générer un wrapper pour toute commande Linux simplement en l'ajoutant à la liste. Si vous ajoutez ce code à votre profil PowerShell, ces commandes seront disponibles dans chaque session PowerShell, tout comme les commandes natives!

    Paramètres par défaut

    Dans Linux, il est courant de définir des alias et/ou des variables d’environnement dans les profils de connexion pour définir les paramètres par défaut des commandes que vous utilisez fréquemment (par exemple, alias ls = ls -AFh ou export LESS = -i). L’un des inconvénients de la procuration par l’intermédiaire d’un shell non interactif via wsl.exe est que les profils de connexion ne sont pas chargés; ces paramètres par défaut ne sont donc pas disponibles (c’est-à-dire que ls dans WSL et wsl ls se comportaient différemment avec l’alias défini ci-dessus).

    PowerShell fournit $PSDefaultParameterValues, un mécanisme standard permettant de définir les valeurs de paramètre par défaut, mais uniquement pour les applets de commande et les fonctions avancées. Convertir nos wrappers de fonctions en fonctions avancées est possible, mais introduit des complications (par exemple, PowerShell fait correspondre les noms de paramètres partiels - -a peut correspondre à -ArgumentList - ce qui entre en conflit avec les commandes Linux acceptant les noms partiels comme arguments), et la syntaxe permettant de définir les valeurs par défaut serait moins qu'idéal pour ce scénario (nécessitant le nom d'un paramètre dans la clé pour définir les arguments par défaut plutôt que le nom de la commande).

    Avec une petite modification de nos wrappers de fonctions, nous pouvons introduire un modèle similaire à $PSDefaultParameterValues et activer les paramètres par défaut pour les commandes Linux!

    Code PowerShell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function global:$_() {
        …
     
        `$defaultArgs = ((`$WslDefaultParameterValues.$_ -split ' '), "")[`$WslDefaultParameterValues.Disabled -eq `$true]
        if (`$input.MoveNext()) {
            `$input.Reset()
            `$input | wsl.exe&nbsp;$_ `$defaultArgs (`$args -split ' ')
        } else {
            wsl.exe&nbsp;$_ `$defaultArgs (`$args -split ' ')
        }
    }

    En transmettant $WslDefaultParameterValues à la ligne de commande que nous envoyons via wsl.exe, vous pouvez maintenant ajouter des instructions comme ci-dessous à votre profil PowerShell pour configurer les paramètres par défaut!

    Code PowerShell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $WslDefaultParameterValues["grep"] = "-E"
    $WslDefaultParameterValues["less"] = "-i"
    $WslDefaultParameterValues["ls"] = "-AFh --group-directories-first"

    Étant donné que ceci est modélisé selon $PSDefaultParameterValues, vous pouvez les désactiver temporairement facilement en définissant la clé "Disabled" sur $true. Une table de hachage séparée présente l’avantage supplémentaire de pouvoir désactiver $WslDefaultParameterValues séparément de $PSDefaultParameterValues.

    Complétion des arguments

    PowerShell vous permet d'inscrire d'activer la complétion d'arguments à l'aide de la commande Register-ArgumentCompleter. Bash dispose de puissantes fonctions de complétion programmables. WSL vous permet d’appeler en bash depuis PowerShell. Si nous pouvons activer la complétion d'arguments pour nos wrappers de fonctions PowerShell et faire des appels bash pour générer les complétions, nous pouvons obtenir une complétion d'arguments enrichie avec la même fidélité que dans bash lui-même !

    Code PowerShell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    # Register an ArgumentCompleter that shims bash's programmable completion.
    Register-ArgumentCompleter -CommandName $commands -ScriptBlock {
        param($wordToComplete, $commandAst, $cursorPosition)
     
        # Map the command to the appropriate bash completion function.
        $F = switch ($commandAst.CommandElements[0].Value) {
            {$_ -in "awk", "grep", "head", "less", "ls", "sed", "seq", "tail"} {
                "_longopt"
                break
            }
     
            "man" {
                "_man"
                break
            }
     
            "ssh" {
                "_ssh"
                break
            }
     
            Default {
                "_minimal"
                break
            }
        }
     
        # Populate bash programmable completion variables.
        $COMP_LINE = "`"$commandAst`""
        $COMP_WORDS = "('$($commandAst.CommandElements.Extent.Text -join "' '")')" -replace "''", "'"
        for ($i = 1; $i -lt $commandAst.CommandElements.Count; $i++) {
            $extent = $commandAst.CommandElements[$i].Extent
            if ($cursorPosition -lt $extent.EndColumnNumber) {
                # The cursor is in the middle of a word to complete.
                $previousWord = $commandAst.CommandElements[$i - 1].Extent.Text
                $COMP_CWORD = $i
                break
            } elseif ($cursorPosition -eq $extent.EndColumnNumber) {
                # The cursor is immediately after the current word.
                $previousWord = $extent.Text
                $COMP_CWORD = $i + 1
                break
            } elseif ($cursorPosition -lt $extent.StartColumnNumber) {
                # The cursor is within whitespace between the previous and current words.
                $previousWord = $commandAst.CommandElements[$i - 1].Extent.Text
                $COMP_CWORD = $i
                break
            } elseif ($i -eq $commandAst.CommandElements.Count - 1 -and $cursorPosition -gt $extent.EndColumnNumber) {
                # The cursor is within whitespace at the end of the line.
                $previousWord = $extent.Text
                $COMP_CWORD = $i + 1
                break
            }
        }
     
        # Repopulate bash programmable completion variables for scenarios like '/mnt/c/Program Files'/<TAB> where <TAB> should continue completing the quoted path.
        $currentExtent = $commandAst.CommandElements[$COMP_CWORD].Extent
        $previousExtent = $commandAst.CommandElements[$COMP_CWORD - 1].Extent
        if ($currentExtent.Text -like "/*" -and $currentExtent.StartColumnNumber -eq $previousExtent.EndColumnNumber) {
            $COMP_LINE = $COMP_LINE -replace "$($previousExtent.Text)$($currentExtent.Text)", $wordToComplete
            $COMP_WORDS = $COMP_WORDS -replace "$($previousExtent.Text) '$($currentExtent.Text)'", $wordToComplete
            $previousWord = $commandAst.CommandElements[$COMP_CWORD - 2].Extent.Text
            $COMP_CWORD -= 1
        }
     
        # Build the command to pass to WSL.
        $command = $commandAst.CommandElements[0].Value
        $bashCompletion = ". /usr/share/bash-completion/bash_completion 2> /dev/null"
        $commandCompletion = ". /usr/share/bash-completion/completions/$command 2> /dev/null"
        $COMPINPUT = "COMP_LINE=$COMP_LINE; COMP_WORDS=$COMP_WORDS; COMP_CWORD=$COMP_CWORD; COMP_POINT=$cursorPosition"
        $COMPGEN = "bind `"set completion-ignore-case on`" 2> /dev/null; $F `"$command`" `"$wordToComplete`" `"$previousWord`" 2> /dev/null"
        $COMPREPLY = "IFS=`$'\n'; echo `"`${COMPREPLY[*]}`""
        $commandLine = "$bashCompletion; $commandCompletion; $COMPINPUT; $COMPGEN; $COMPREPLY" -split ' '
     
        # Invoke bash completion and return CompletionResults.
        $previousCompletionText = ""
        (wsl.exe $commandLine) -split '\n' |
        Sort-Object -Unique -CaseSensitive |
        ForEach-Object {
            if ($wordToComplete -match "(.*=).*") {
                $completionText = Format-WslArgument ($Matches[1] + $_) $true
                $listItemText = $_
            } else {
                $completionText = Format-WslArgument $_ $true
                $listItemText = $completionText
            }
     
            if ($completionText -eq $previousCompletionText) {
                # Differentiate completions that differ only by case otherwise PowerShell will view them as duplicate.
                $listItemText += ' '
            }
     
            $previousCompletionText = $completionText
            [System.Management.Automation.CompletionResult]::new($completionText, $listItemText, 'ParameterName', $completionText)
        }
    }
     
    # Helper function to escape characters in arguments passed to WSL that would otherwise be misinterpreted.
    function global:Format-WslArgument([string]$arg, [bool]$interactive) {
        if ($interactive -and $arg.Contains(" ")) {
            return "'$arg'"
        } else {
            return ($arg -replace " ", "\ ") -replace "([()|])", ('\$1', '`$1')[$interactive]
        }
    }

    En gros :
    • Nous enregistrons l'argument compléteur pour tous nos wrappers de fonction en passant la liste $commands au paramètre -CommandName de Register-ArgumentCompleter ;
    • Nous mappons chaque commande à la fonction shell utilisée par bash pour la compléter ($F qui porte le nom de complete -F <FUNCTION> utilisée pour définir les spécifications de complétion dans bash) ;
    • Nous convertissons les arguments $wordToComplete, $commandAst et $cursorPosition de PowerShell au format attendu par les fonctions de complétion bash conformément à la spécification de complétion programmable bash ;
    • Nous construisons une ligne de commande que nous pouvons transmettre à wsl.exe afin de nous assurer que l'environnement d'achèvement est correctement configuré, d'appeler la fonction de complétion appropriée, puis de générer une chaîne contenant les résultats d'achèvement séparés par de nouvelles lignes ;
    • Nous appelons ensuite wsl avec la ligne de commande, divisons la chaîne de sortie sur le nouveau séparateur de ligne, puis générons CompletionResults pour chacun, en les triant, ainsi que des caractères d'échappement tels que des espaces et des parenthèses qui seraient autrement mal interprétés.

    Le résultat final de ceci est maintenant que nos wrappers de commandes Linux utiliseront exactement la même finition que celle utilisée par bash ! Par exemple :

    Code Powershell : Sélectionner tout - Visualiser dans une fenêtre à part
    ssh -c <TAB> -J <TAB> -m <TAB> -O <TAB> -o <TAB> -Q <TAB> -w <TAB> -b <TAB>

    Chaque complétion va fournir des valeurs spécifiques à l'argument précédent, lisant des données de configuration comme des hôtes connus à partir de WSL!

    <TAB> va parcourir les options. <Ctrl + Espace> affichera toutes les options disponibles.

    De plus, étant donné que la complétion bash est désormais supportée, vous pouvez résoudre les chemins Linux directement dans PowerShell !
    • less /etc/<TAB>
    • ls /usr/share/<TAB>
    • vim ~/.bash<TAB>

    Dans les cas où la complétion bash ne renvoie aucun résultat, PowerShell reprend sa complétion par défaut, ce qui résoudra les chemins Windows, ce qui vous permettra effectivement de résoudre à la fois les chemins Linux et Windows.

    Mike conclut en disant que : « avec PowerShell et WSL, nous pouvons intégrer des commandes Linux à Windows comme s'il s'agissait d'applications natives. Nul besoin de chercher des versions Win32 d'utilitaires Linux ni d'être obligé d'interrompre votre flux de travail pour accéder à un shell Linux. Installez simplement WSL, configurez votre profil PowerShell et répertoriez les commandes que vous souhaitez importer! La complétion riche des arguments sur les options de commande et les chemins de fichiers Linux et Windows présentée ici est une expérience que même les commandes Windows natives ne fournissent pas aujourd’hui ».

    code source complet de ce qui a été décrit ici ainsi qu'un guide pour vous en servir dans votre workflow

    Source : Microsoft

    Et vous ?

    Avez-vous déjà souhaité intégrer des commandes Linux à Windows ?
    Que pensez-vous de l'approche de cet ingénieur ?
    Allez-vous l'essayer ou préférez-vous attendre la disponibilité d'un utilitaire spécifique qui va faire ce travail ?

    Voir aussi :

    Comment profiter de Linux sur Windows 10 sans WSL sur le Microsoft Store avec Dark Moon ?
    Des ingénieurs demandent à ce que soient ajoutés les opérateurs de type Bash && et || à PowerShell dans une RFC
    Microsoft publie le code source du noyau Linux léger utilisé dans WSL2 sous licence GPL version 2
    Docker Desktop pour Windows 10 va bientôt passer à WSL2, quels sont les avantages pour les développeurs ?
    Microsoft avance que la prochaine itération de PowerShell Core sera baptisée PowerShell 7, l'éditeur explique pourquoi il envisage de supprimer "Core"
    Contribuez au club : Corrections, suggestions, critiques, ... : Contactez le service news et Rédigez des actualités

  2. #2
    Membre extrêmement actif
    Avatar de Sodium
    Femme Profil pro
    Développeuse web
    Inscrit en
    avril 2014
    Messages
    1 725
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeuse web

    Informations forums :
    Inscription : avril 2014
    Messages : 1 725
    Points : 1 681
    Points
    1 681
    Billets dans le blog
    1
    Par défaut
    Je confirme pour l'amélioration spectaculaire du système de fichier. Sur une application Symfony qui charge beaucoup trop de dépendances à mon goût, je suis passée de 12 secondes de chargement à... 800ms.

  3. #3
    Membre régulier
    Homme Profil pro
    Chercheur en informatique
    Inscrit en
    août 2018
    Messages
    50
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Chercheur en informatique

    Informations forums :
    Inscription : août 2018
    Messages : 50
    Points : 116
    Points
    116
    Par défaut
    C'est bien.
    On attend la sortie publique de Windows Terminal et de WSL2.
    2020 peut-être ?

Discussions similaires

  1. Utiliser des commandes linux dans un shell
    Par Info_76 dans le forum Shell et commandes GNU
    Réponses: 4
    Dernier message: 20/03/2008, 12h03
  2. Réponses: 3
    Dernier message: 29/01/2008, 14h28
  3. interface graphique pour des commandes linux
    Par A7med dans le forum Applications et environnements graphiques
    Réponses: 2
    Dernier message: 24/10/2007, 16h49
  4. Peut-on utiliser des commandes linux avec PERL
    Par undercrash dans le forum Linux
    Réponses: 2
    Dernier message: 01/03/2007, 14h12
  5. [FreePascal] Interpréter des commandes Linux
    Par llaurentt dans le forum Free Pascal
    Réponses: 8
    Dernier message: 23/01/2006, 18h08

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo