C# - SQLCMD 不识别标准输入
C# - SQLCMD didn't recognised standard input
如果您没有为 SQLCMD 传递任何密码,它会尝试从标准输入读取它(我可以手动完成并且它有效)。
我启动了 cmd 进程,并将密码作为标准输入传递给 streamWriter。
进程正确启动,我可以打印通过的流,但我得到了
Sqlcmd: Error: Microsoft ODBC Driver 17 for SQL Server : Login failed for user 'sa'
有没有办法让 sqlcmd 读取标准输入?
C#代码
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = @"path_to_my_script",
UseShellExecute = false,
CreateNoWindow = false,
RedirectStandardInput = true
};
var process = Process.Start( processStartInfo );
StreamWriter myStreamWriter = process.StandardInput;
string pass = "123";
myStreamWriter.WriteLine( pass );
myStreamWriter.Close();
myScript(修改后的查询)
SqlCmd -S DESKTOP-UR7LHEE -U sa -Q "SELECT * FROM myDb"
下面介绍如何使用System.Diagnostics.Process to run a SQL script using sqlcmd. However, you may want to see if Server Management Objects (SMO)满足您的需求。
以下已测试:
添加以下using语句:
- 使用 Microsoft.Win32;
- 使用 System.IO;
- 使用 System.Diagnostics;
首先,我们将创建一个方法来查询注册表以找到 sqlcmd.exe:
的路径
注意:如果sqlcmd.exe所在的directory/folder在你的PATH环境变量中,那么下面的方法就不用了——可以用" sqlcmd.exe" 而不是 fully-qualified 文件名。
GetSqlCmdPath:
private string GetSqlCmdPath()
{
//get the fully-qualified path for sqlcmd.exe
string sqlCmdPath = string.Empty;
using (RegistryKey key = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64))
{
using (RegistryKey subkey = key.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server"))
{
if (subkey != null)
{
string[] subkeyNames = subkey.GetSubKeyNames();
if (subkeyNames != null)
{
foreach (string name in subkeyNames)
{
string clientSetupSubkey = Path.Combine(name, "Tools", "ClientSetup");
using (RegistryKey subkeyClientSetup = subkey.OpenSubKey(clientSetupSubkey))
{
if (subkeyClientSetup != null)
{
string[] valueNames = subkeyClientSetup.GetValueNames();
if (valueNames != null)
{
foreach (string vName in valueNames)
{
if (vName == "Path" || vName == "ODBCToolsPath")
{
//get value
string valPath = subkeyClientSetup.GetValue(vName)?.ToString();
//check if sqlcmd.exe exists
if (File.Exists(Path.Combine(valPath, "sqlcmd.exe")))
{
sqlCmdPath = Path.Combine(valPath, "sqlcmd.exe");
}
}
}
}
}
}
}
}
}
}
}
return sqlCmdPath;
}
接下来,创建一个使用 Process 实例执行脚本的方法:
RunProcessSqlCmd:
private void RunProcessSqlCmd(string scriptFilename, string logFilename, string password)
{
string sqlCmdPath = GetSqlCmdPath();
if (String.IsNullOrEmpty(sqlCmdPath))
throw new Exception("Error: Fully-qualified path to 'sqlcmd.exe' could not be determined.");
ProcessStartInfo startInfo = new ProcessStartInfo ()
{
Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0} -o {1}", scriptFilename, logFilename),
//Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0}", scriptFilename),
CreateNoWindow = true,
FileName = sqlCmdPath,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
};
using (Process p = new Process () { StartInfo = startInfo, EnableRaisingEvents = true})
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
}
};
//start
p.Start();
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
using (StreamWriter sw = p.StandardInput)
{
//provide values for each input prompt
//ToDo: add values for each input prompt - changing the for loop as necessary
//Note: Since we only have 1 prompt, using a loop is unnecessary - a single 'WriteLine' statement would suffice
for (int i = 0; i < 1; i++)
{
if (i == 0)
sw.WriteLine(password); //1st prompt
else
break; //exit
}
}
//waits until the process is finished before continuing
p.WaitForExit();
}
}
注意:上面的代码将脚本的输出写入日志文件。如果您更愿意将输出写入 StandardOutput,请更改以下内容:
来自:
Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0} -o {1}", scriptFilename, logFilename),
到:
Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0}", scriptFilename),
此外,由于logFilename
参数将不再使用,您可以将其删除。
用法:
//create test script
string logFilename = Path.Combine(Path.GetTempPath(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + "_ScriptLog.txt");
string scriptFilename = Path.Combine(Path.GetTempPath(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + "_Script.sql");
Debug.WriteLine($"scriptFilename: {scriptFilename} logFilename: {logFilename}");
//string scriptText = "use master;" + System.Environment.NewLine;
//scriptText += "SELECT name, database_id from sys.databases;";
string scriptText = "SELECT name, database_id from sys.databases;";
File.WriteAllText(scriptFilename, scriptText);
RunProcessSqlCmd(scriptFilename, logFilename, "myPassword123");
资源:
- System.Diagnostics.Process
- Download sqlcmd utility
- sqlcmd - Use the utility
- View list of databases on SQL Server
服务器管理对象 (SMO) 资源:
如果您没有为 SQLCMD 传递任何密码,它会尝试从标准输入读取它(我可以手动完成并且它有效)。
我启动了 cmd 进程,并将密码作为标准输入传递给 streamWriter。
进程正确启动,我可以打印通过的流,但我得到了
Sqlcmd: Error: Microsoft ODBC Driver 17 for SQL Server : Login failed for user 'sa'
有没有办法让 sqlcmd 读取标准输入?
C#代码
ProcessStartInfo processStartInfo = new ProcessStartInfo
{
FileName = @"path_to_my_script",
UseShellExecute = false,
CreateNoWindow = false,
RedirectStandardInput = true
};
var process = Process.Start( processStartInfo );
StreamWriter myStreamWriter = process.StandardInput;
string pass = "123";
myStreamWriter.WriteLine( pass );
myStreamWriter.Close();
myScript(修改后的查询)
SqlCmd -S DESKTOP-UR7LHEE -U sa -Q "SELECT * FROM myDb"
下面介绍如何使用System.Diagnostics.Process to run a SQL script using sqlcmd. However, you may want to see if Server Management Objects (SMO)满足您的需求。
以下已测试:
添加以下using语句:
- 使用 Microsoft.Win32;
- 使用 System.IO;
- 使用 System.Diagnostics;
首先,我们将创建一个方法来查询注册表以找到 sqlcmd.exe:
的路径注意:如果sqlcmd.exe所在的directory/folder在你的PATH环境变量中,那么下面的方法就不用了——可以用" sqlcmd.exe" 而不是 fully-qualified 文件名。
GetSqlCmdPath:
private string GetSqlCmdPath()
{
//get the fully-qualified path for sqlcmd.exe
string sqlCmdPath = string.Empty;
using (RegistryKey key = RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, RegistryView.Registry64))
{
using (RegistryKey subkey = key.OpenSubKey(@"SOFTWARE\Microsoft\Microsoft SQL Server"))
{
if (subkey != null)
{
string[] subkeyNames = subkey.GetSubKeyNames();
if (subkeyNames != null)
{
foreach (string name in subkeyNames)
{
string clientSetupSubkey = Path.Combine(name, "Tools", "ClientSetup");
using (RegistryKey subkeyClientSetup = subkey.OpenSubKey(clientSetupSubkey))
{
if (subkeyClientSetup != null)
{
string[] valueNames = subkeyClientSetup.GetValueNames();
if (valueNames != null)
{
foreach (string vName in valueNames)
{
if (vName == "Path" || vName == "ODBCToolsPath")
{
//get value
string valPath = subkeyClientSetup.GetValue(vName)?.ToString();
//check if sqlcmd.exe exists
if (File.Exists(Path.Combine(valPath, "sqlcmd.exe")))
{
sqlCmdPath = Path.Combine(valPath, "sqlcmd.exe");
}
}
}
}
}
}
}
}
}
}
}
return sqlCmdPath;
}
接下来,创建一个使用 Process 实例执行脚本的方法:
RunProcessSqlCmd:
private void RunProcessSqlCmd(string scriptFilename, string logFilename, string password)
{
string sqlCmdPath = GetSqlCmdPath();
if (String.IsNullOrEmpty(sqlCmdPath))
throw new Exception("Error: Fully-qualified path to 'sqlcmd.exe' could not be determined.");
ProcessStartInfo startInfo = new ProcessStartInfo ()
{
Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0} -o {1}", scriptFilename, logFilename),
//Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0}", scriptFilename),
CreateNoWindow = true,
FileName = sqlCmdPath,
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden
};
using (Process p = new Process () { StartInfo = startInfo, EnableRaisingEvents = true})
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
}
};
//start
p.Start();
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
using (StreamWriter sw = p.StandardInput)
{
//provide values for each input prompt
//ToDo: add values for each input prompt - changing the for loop as necessary
//Note: Since we only have 1 prompt, using a loop is unnecessary - a single 'WriteLine' statement would suffice
for (int i = 0; i < 1; i++)
{
if (i == 0)
sw.WriteLine(password); //1st prompt
else
break; //exit
}
}
//waits until the process is finished before continuing
p.WaitForExit();
}
}
注意:上面的代码将脚本的输出写入日志文件。如果您更愿意将输出写入 StandardOutput,请更改以下内容:
来自:
Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0} -o {1}", scriptFilename, logFilename),
到:
Arguments = String.Format(@"-S .\SQLExpress -U appAdmin -i {0}", scriptFilename),
此外,由于logFilename
参数将不再使用,您可以将其删除。
用法:
//create test script
string logFilename = Path.Combine(Path.GetTempPath(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + "_ScriptLog.txt");
string scriptFilename = Path.Combine(Path.GetTempPath(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + "_Script.sql");
Debug.WriteLine($"scriptFilename: {scriptFilename} logFilename: {logFilename}");
//string scriptText = "use master;" + System.Environment.NewLine;
//scriptText += "SELECT name, database_id from sys.databases;";
string scriptText = "SELECT name, database_id from sys.databases;";
File.WriteAllText(scriptFilename, scriptText);
RunProcessSqlCmd(scriptFilename, logFilename, "myPassword123");
资源:
- System.Diagnostics.Process
- Download sqlcmd utility
- sqlcmd - Use the utility
- View list of databases on SQL Server
服务器管理对象 (SMO) 资源: