Il n'y a pas de commande 'sudo' dans Cygwin

41

Parce qu'il n'y a pas de commande sudo dans Cygwin , les scripts que je veux exécuter échouent

./install.sh: line N: sudo: command not found

Quel est le moyen standard pour contourner cela? Modification des scripts à supprimer sudo? Obtenir un sudooutil similaire à Windows?

Jason Sundram
la source
@dotancohen, j'espère avoir choisi le bon.
Jason Sundram
Cela me semble une bonne solution! L’appui de Cygwin s’est certainement amélioré au cours des cinq dernières années!
dotancohen
@Benj, je me demande pourquoi cette question n'a pas été migrée ici aussi.
Jason Sundram
@ JasonSundram en effet. Faites-moi savoir si la réponse est déplacée, je mettrai alors à jour le lien.
Benj

Réponses:

8

J'ai écrit TOUACExt pour SUDO (plutôt simple) pour CygWin , une automatisation de script shell pré-bêta qui aborde le comportement de classicsudo sous Linux:

  • Ouvre et ferme automatiquement sudoserver.py si nécessaire.
  • Demande l' invite d'élévation UAC .

L'installation nécessite la copie des quatre .shscripts dans un répertoire de chemin, la création d'un alias et quelques étapes supplémentaires détaillées dans le fil de discussion.

Le résultat : vous tapez un simple sudo YourCommandet vous en obtenez le résultat sans avoir à vous soucier du reste du processus.

Sopalajo de Arrierez
la source
35

Une solution consiste à créer une fausse commande "sudo" avec le contenu suivant:

#!/usr/bin/bash

"$@"

Cela permettra install.shà continuer, car sudo est trouvé.

Cela n'élève pas les privilèges comme le fait le vrai sudo. Si vous avez vraiment besoin de privilèges élevés, démarrez cygwin shell à partir d'un compte doté de privilèges d'administrateur (XP) ou cliquez avec le bouton droit de la souris sur cygwin.bat et exécutez-le en tant qu'administrateur (Vista, Win7).

Péon
la source
5
Juste par curiosité de la part de quelqu'un qui ne parle pas couramment bash: pourquoi cela fonctionne-t-il? La page de manuel ne dit rien sur le $@fait de faire quoi que ce soit sudo. Au lieu de cela, ce ne sont que tous les arguments du script. Et les citations autour de cela ne seraient-elles pas superflues dans ce cas? Sinon, si vous le faites, sudo foo baril essaie de s'exécuter en "foo bar"tant que commande unique, ce qui n'existe probablement pas étant donné la peur irrationnelle des espaces sur les systèmes de type UNIX.
Joey
7
@Johannes: "$@"(lorsqu'il est cité entre guillemets) fonctionne différemment de "$*": il se développe en un mot distinct pour chaque variable de position. Exemple: Si $1 == "foo bar"et $2 == "baz", alors "$@"est "foo bar" baz- un mot pour chaque paramètre (contrairement à "$*", ce qui donne "foo bar baz"un mot). Voir le manuel de bash, section Paramètres , sous- section Paramètres spéciaux . Le résultat final du script de Peon est qu'il exécute ses arguments exactement tels qu'ils ont été passés.
Grawity
1
Ah ok. Et où intervient la sudopièce? L'extrait ci-dessus ne fait rien à distance dans cette direction, non?
Joey
2
@ Johannes: Sous Unix, un réel sudolève les privilèges de mortal à rootavant d'exécuter la commande. Dans Cygwin, cela n'existe pas, aussi le faux script de Peon (que vous êtes censé nommer sudo) exécute simplement la commande directement sans modifier ses privilèges. (Cela signifie que vous devrez peut-être exécuter en ./install.shtant qu'administrateur.)
grawity
2
@grawity: runasdevrait fonctionner, il ne dépend pas de l'UAC et demande un mot de passe par lui-même. J'étais juste confus pourquoi le script dans la réponse apparemment ne faisait pas ce que le nom sous-entendait que je supposais être le but. Désolé pour ma stupidité ;-)
Joey
21

J'ai trouvé la réponse sur la liste de diffusion de cygwin . Pour exécuter commandavec des privilèges élevés dans Cygwin, faites précéder la commande de la manière cygstart --action=runassuivante:

$ cygstart --action=runas command

Cela ouvrira une boîte de dialogue Windows demandant le mot de passe administrateur et exécutera la commande si le mot de passe correct est entré.

Ceci est facilement scripté, tant qu'il ~/binest dans votre chemin:

$ cat ~/bin/sudo
#!/usr/bin/bash
cygstart --action=runas "$@"

$ PATH=$HOME/bin:$PATH
$ chmod +x ~/bin/sudo
$ sudo elevatedCommand

Testé sous Windows 8 64 bits.

dotancohen
la source
5
Le problème avec cette cygstartméthode est qu’elle ne fonctionne que pour les commandes / programmes Windows. Tu ne peux pas faire sudo ls. SUDO pour CygWin est soigné, mais manque toujours de sudomaîtrise.
Sopalajo de Arrierez
Merci, Sopalajode. Dans quelle situation avez-vous eu besoin d'utiliser sudo lsCygwin?
dotancohen
3
Oh non, @Dotancohen, ce n'était qu'un exemple. Vous pouvez utiliser sudopour CygWin pour exécuter n’importe quelle commande Windows ou CygWin. Ça m'est très utile. Mais la méthode la plus pratique que j'ai trouvée est cette enveloppe de script pour SUDO pour CygWin que j'ai développée: superuser.com/questions/741345/… (toujours en version bêta, mais semble fonctionner). Avec cela, vous pouvez facilement commander des choses comme sudo net start vncserver.
Sopalajo de Arrierez
@SopalajodeArrierez: C'est absolument génial! Merci pour le post et le lien.
dotancohen
curieusement, cela dénude /binet /usr/binde la PATH. Il appelle avec succès emacs: ShellExecute(NULL, "runas", "C:\cygwin64\bin\emacs-w32.exe", "(null)", "(null)", 1)mais emacs ne peut pas rechercher, lspar exemple M-x dired, même après la restauration interactive de PATH à l'aide de (setenv ...). Existe-t-il un problème de chemins de confiance ici?
BaseZen
5

En me basant sur la réponse de dotancohen, j'utilise un alias:

alias sudo="cygstart --action=runas"

Fonctionne comme un charme pour les programmes externes (pas les shell intégrés):

sudo chown User:Group <file>
thoni56
la source
3

Sudo (Elevate) pour Windows ™

Je travaille beaucoup sur la ligne de commande sous Windows ™.

Dans Cygwin lui-même, je pense que vous pouvez exécuter une commande racine avec su -c /the/cmdcomme pour sudo lui-même dans le système de fichiers Windows ™ en élevant les autorisations de l'utilisateur depuis la ligne de commande. Si vous êtes un administrateur, cela fonctionnera très bien pour vous. Sinon, utilisez les runas et obtenez le passe administrateur;).

Maintenant, je ne me souviens plus où nous avons obtenu ce code, mais le voici. J'espère que ça aide.

BTW, le paquet que nous avons utilisé pour compiler était gcc-mingw32.

$ i586-mingw32msvc-gcc sudo.c -o sudo.exe
# Put sudo.exe in /usr/bin or in your windows path (%homedrive%\windows)
#example:
$ sudo vi /cygdrive/c/windows/system32/drivers/etc/hosts

/**
* (sudo for Windows™)
* @filename sudo.c
*/
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
#include <shellapi.h>
#include <wchar.h>


LPWSTR *mergestrings(LPWSTR *left, LPCWSTR right)
{
    size_t size = ( 1 + lstrlen(*left) + lstrlen(right) ) * sizeof(LPWSTR*);
    if ( *left ) {
        LPWSTR leftcopy = _wcsdup(*left);
        *left = (LPWSTR)realloc(*left, size);
        *left = lstrcpy(*left, leftcopy);
        *left = lstrcat(*left, right);
        free( leftcopy );
    }
    else
        *left = _wcsdup(right);
    return left;
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR lpcommand, int nShowCmd)
{
    DWORD result = 0x2a;
    LPWSTR *argv = NULL;
    int argc = 0;
    if ( argv = CommandLineToArgvW(GetCommandLineW(), &argc) ) {
        if ( argc < 2 ) {
            LPWSTR usagemsg = NULL;
            usagemsg = *mergestrings(&usagemsg, argv[0]);
            usagemsg = *mergestrings(&usagemsg, TEXT(" <command_to_run> [arguments]"));
            MessageBox(NULL, usagemsg, TEXT("Usage:"), MB_OK | MB_ICONEXCLAMATION );
            LocalFree( argv );
            free( usagemsg );
            return ERROR_BAD_ARGUMENTS;
        }
        else {
            LPWSTR command = argv[1];
            LPWSTR arguments = NULL;
            int c;
            for ( c = 2; c < argc; c++ ) {
                arguments = *mergestrings(&arguments, argv[c]);
                arguments = *mergestrings(&arguments, TEXT(" "));
            }
            result = (DWORD)ShellExecute(NULL, TEXT("runas"), command, arguments, NULL, SW_SHOWNORMAL);
            LocalFree( argv );
            if ( arguments )
                free( arguments );
            switch ( result )
            {
                case 0:
                    result = ERROR_OUTOFMEMORY;
                    break;

                case 27:
                case 31:
                    result = ERROR_NO_ASSOCIATION;
                    break;

                case 28:
                case 29:
                case 30:
                    result = ERROR_DDE_FAIL;
                    break;
                case 32:
                    result = ERROR_DLL_NOT_FOUND;
                    break;
                default:
                    if ( result > 32 )
                        result = 0x2a;
            }
        }
    }
    else
        result = GetLastError();

    if (result != 0x2a) {
        LPWSTR errormsg = NULL;
        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
                      NULL, result, 0, (LPWSTR)&errormsg, 0, NULL);
        MessageBox(NULL, errormsg, TEXT("Error:"), MB_OK | MB_ICONERROR);
        LocalFree( errormsg );
        return result;
    }
    else
        return NO_ERROR;
}
tao
la source
5
Ce code est affreux. C'est plein d'erreurs telles que ne pas vérifier la valeur de retour de realloc () avant de déréférencer ou d'écrire sizeof (LPWSTR *) au lieu de sizeof (* LPWSTR) où LPWSTR semble être un type de pointeur et que l'on veut récupérer la taille d'un caractère, pas la taille du pointeur. De plus, on ne comprend pas du tout pourquoi, par exemple, le cas 29 mène à ERROR_DDE_FAIL. Pouvez-vous conclure du code pourquoi? Je ne peux pas et je suppose que tout le monde ne le peut pas aussi. S'il vous plaît, ne postez pas ce code à l'avenir.
4
@ Mattew: aidez la communauté à l'avenir en postant une version nettoyée de l'extrait de code que vous n'aimez pas.
Erik Allik
Le code ne devrait pas être sur le superutilisateur. Mettez-le sur codereview.se, et il suffit de le lier à partir d'ici.
Ben Voigt
@ user185282: Bons points. J'ai voté contre la réponse.
inoubliable
Cher tao: Vous avez écrit: "Je ne me souviens pas d'où nous avons obtenu ce code". Avez-vous écrit ce code ou quelqu'un d'autre l'a-t-il écrit?
inoubliable
2

Une légère amélioration sur le faux script sudo de Peon :

#!/bin/sh
# Drop any option arguments.
while [[ $# -ge 0 && $1 = -* ]]; do
  shift
done

"$@"

Ce script supprime en silence toutes les options passées à sudo et exécute la commande (sans élever réellement les privilèges). Supprimer les options améliore quelque peu la compatibilité. Un script d'encapsulation plus complet devrait en réalité analyser les options de la même façon que sudo.

Au lieu d'essayer de remplacer sudo par un wrapper cygstart --action=runas "$@", utilisez simplement ce faux wrapper sudo et exécutez votre script d'installation lui-même (ou tout ce que vous essayez d'utiliser qui utilise sudo) avec des privilèges élevés.

Gene Pavlovsky
la source