web-dev-qa-db-fra.com

Obtenir les résultats de XP_CMDSHELL

J'ai fait des recherches sur le Web et il semble que la seule façon d'obtenir les résultats de XP_CMDSHELL est de les stocker dans une table temporaire. N'y a-t-il vraiment aucun moyen plus simple?

De Experts Exchange:

Non, xp_cmdshell ne renverra aucune information de l'exe. et vous devez utiliser la syntaxe suivante si vous n'êtes pas dans la base de données master pour l'exécuter. master..xp_cmdshell. Vous devrez donner à votre utilisateur l'autorisation d'exécuter cette procédure dans la base de données master. Vous devrez demander à votre exe d'insérer lui-même les informations car il ne peut pas renvoyer d'informations au processus qui les a appelées.

Et...

Alors que @result obtient uniquement la valeur de retour de xp_cmdshell, vous pouvez peut-être capturer les résultats de la commande en insérant directement dans une table ... quelque chose comme ceci:

ymmv ...

set nocount on
declare  @filepath   varchar(255),
         @cmd        varchar(255),
         @rc         int

select   @filepath = 'c:\temp\'         
select   @cmd      = 'dir ' + @filepath + '~*.tmp'

create table #output (output varchar(255) null)
insert #output exec @rc = master..xp_cmdshell @cmd
select * from #output where output is not null
drop table #output
15
Saint Ronin

Il n'y a pas de moyen plus simple de capturer les commentaires STDOUT/STDERR de xp_cmdshell; il y a au moins une alternative mais elle ne pourrait pas être classée comme plus simple:
Il serait possible de rediriger la sortie de la commande vers un fichier texte dans le cadre de la commande, puis de lire le fichier texte à l'aide de OPENROWSET.

BTW il y a au moins une erreur dans le script posté ci-dessus. Les documents pour xp_cmdshell indique qu'elle renvoie la sortie de la commande sous la forme nvarchar (255).
De plus, la table temporaire doit avoir une colonne d'identité, sinon les résultats risquent de ne pas s'afficher dans le bon ordre:

...
create table #output (id int identity(1,1), output nvarchar(255) null)
insert #output (output) exec @rc = master..xp_cmdshell @cmd
select * from #output where output is not null order by id
drop table #output
20
Ed Harper

C'est ce que j'ai fini par faire ... J'ai vérifié aujourd'hui et j'ai vu votre réponse. Hier, j'étais en crise en temps réel, alors j'ai commencé à travailler dans le sens de la table temporaire, car c'était une solution de travail confirmée. J'avais choisi d'éviter de créer les fichiers temporaires car il semblait tout aussi facile ou plus facile de gérer les choses en interne car je ne les utilise vraiment que comme presse-papiers. La seule modification que j'apporterai si nécessaire est d'ajouter un numéro unique au nom de la table temporaire bien que je ne pense pas que je doive m'inquiéter de leur traitement simultané (ce qui signifie qu'un deuxième appel de la procédure stockée pourrait vider la table temporaire pendant que le shell cmd est en cours d'exécution). Nous verrons...

J'appelle une procédure stockée (regardez plus bas) pour crypter le mot de passe: Le code ci-dessous a été modifié pour le rendre autosuffisant. Je ne configure pas réellement le mot de passe manuellement car il s'agit essentiellement d'une solution de déchargement/synchronisation de mot de passe.

DECLARE @password       VARCHAR(64)
DECLARE @encryptedpass  VARCHAR(128);

SET @password = '1234'

BEGIN TRY
    EXEC pass_encrypt @password, @encryptedpass = @encryptedpass OUTPUT
END TRY
BEGIN CATCH
    PRINT 'ERROR'
    RETURN
END CATCH
SELECT @encryptedpass

Voici la procédure stockée de cryptage: Pour vérifier et vous assurer que le programme s'exécute correctement sans avoir à deviner pourquoi le code de retour indique un échec, j'ai un code supplémentaire (non répertorié ici) qui vérifie @@ rowet. S'il est supérieur à 1, je sais que quelque chose s'est mal passé et je peux capturer/renvoyer l'erreur réelle (si vous le souhaitez) au lieu de simplement créer mon propre message qui dit qu'il a échoué sans donner d'indication sur la raison. La vérification réaliste de cette manière est plus utile pour le débogage ou pour consigner l'erreur dans une autre table pour une révision future - pas pour une interprétation en temps réel car je ne vais pas renvoyer une erreur comme celle-ci à un utilisateur final.

USE [**my_database**]
GO

SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO


CREATE procedure [dbo].[pass_encrypt]
(   @password       VARCHAR(64),
    @encryptedpass  VARCHAR(128) OUTPUT
)
AS
BEGIN
    DECLARE @command        VARCHAR(200)
    SET @command = **'C:\encrypt_pwd.exe**' + ' "' + @password + '"'

    BEGIN
        IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[#temppass]') AND type in (N'U')) 
            DROP TABLE [dbo].[#temppass]
        BEGIN TRY
            CREATE TABLE #temppass(encrypted varchar(1000))
            INSERT INTO #temppass execute xp_cmdshell @command
            IF (@@ROWCOUNT > 1)
                BEGIN
                    SET @encryptedpass = NULL
                    DROP TABLE #temppass
                    RETURN
                END
            ELSE
                BEGIN
                    SELECT @encryptedpass = encrypted FROM #temppass
                END
            --SELECT @encryptedpass
        END TRY
        BEGIN CATCH
            SET @encryptedpass = NULL
            DROP TABLE #temppass
            RETURN
        END CATCH
        --SELECT encrypted FROM #temppass
        DROP TABLE #temppass
    END
END
1
Saint Ronin