Получить результаты от XP_CMDSHELL
Я искал веб-страницы, и кажется, что единственный способ получить результаты от XP_CMDSHELL - это сохранить их в таблице temp. Нет ли более простого способа?
От обмена экспертами:
Нет, xp_cmdshell не вернет никакой информации из exe. и вы должны использовать следующие если вы не находитесь в основной базе данных для ее запуска. master..xp_cmdshell. Тебе придется предоставить пользователю разрешение на выполнение этой процедуры в основной базе данных. Вы должны будете ваш exe вставляет информацию сам, потому что он не может вернуть информацию в процесс, который называл это.
А...
В то время как @result получает только возвращаемое значение из xp_cmdshell, вы можете получить результаты
команды, вставляя непосредственно в таблицу... что-то вроде этого:
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
Ответы
Ответ 1
Нет более простого способа захвата обратной связи STDOUT/STDERR от xp_cmdshell
; существует хотя бы одна альтернатива, но ее нельзя классифицировать как проще:
Можно было бы перенаправить вывод команды в текстовый файл как часть команды, затем прочитать текстовый файл с помощью OPENROWSET
.
Кстати, в script есть по крайней мере одна ошибка. Документы для xp_cmdshell
утверждают, что он возвращает вывод команды как nvarchar (255).
Кроме того, таблица temp должна иметь столбец идентификатора, иначе результаты могут не отображаться в правильном порядке:
...
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
Ответ 2
Это то, что я закончил делать... Я проверил сегодня и увидел ваш ответ. Вчера я был в хрусте в реальном времени, поэтому начал работать в направлении временной таблицы, так как это было подтвержденное рабочее решение. Я решил не создавать временные файлы, так как казалось, что это просто так же легко или просто обрабатывать вещи, потому что я просто использую его как буфер обмена. Одно изменение, которое я сделаю в случае необходимости, - это добавить уникальный номер в имя таблицы temp, хотя я не думаю, что мне приходится беспокоиться о том, что они обрабатываются одновременно (это означает, что второй вызов хранимой процедуры может сбрасывать временную таблицу во время запущена оболочка cmd). Посмотрим...
Я вызываю хранимую процедуру (смотрите дальше), чтобы зашифровать пароль:
Приведенный ниже код был изменен, чтобы сделать его самодостаточным. Я фактически не устанавливаю пароль вручную, так как это в основном решение для разгрузки/синхронизации паролей.
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
Вот хранимая процедура шифрования:
Чтобы проверить и убедиться, что программа выполняется правильно, не догадываясь, почему код возврата указывает на сбой, у меня есть дополнительный код (не указан здесь), который проверяет @@rowset. Если это больше, чем 1, я знаю, что что-то пошло не так, и я могу захватить/вернуть фактическую ошибку (при желании) вместо того, чтобы просто составить собственное сообщение, в котором говорится, что он не прошел, не указав причины. Реалистичная проверка этого пути более полезна для отладки или для регистрации ошибки в другой таблице для будущего обзора, а не для интерпретации в режиме реального времени, поскольку я не собираюсь отправлять такую ошибку обратно конечному пользователю.
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