在将值传递给SQL之前,在PowerShell中转义字符串中的单引号

y1aodyip  于 2023-06-23  发布在  Shell
关注(0)|答案(2)|浏览(252)

我有一个脚本,它可以检索与给定文件夹路径中的文件相关的信息,然后通过SQLPS模块和SQLINSERT INTO命令传递这些值来填充我的数据库表。
除了文件或文件夹路径包含一个离代的少数情况外,这都可以正常工作。
相关代码块为

$PathArowCount=0
$filelist = Get-ChildItem -Path $localPath -Recurse | Where-Object {!($_.PSIsContainer)} | Select DirectoryName, Name, Length, LastWriteTime 
ForEach ($file in $filelist)
{
    # Retrieve all values into variables. Value passed to the insert statement needs to be within single quotes, so escaping them out here so they're included later.
    $insFolder = "`'$($file.DirectoryName)`'"
    $insFile = "`'$($file.Name)`'"
    $insLength = "`'$($file.Length)`'"
    $insLastWrite = "`'$($file.LastWriteTime)`'"
    write-host "$insFolder`n$insFile`n$insLength`n$insLastWrite`n---"
    # Generate the SQL insert statement using the values above
    $sqlCommand.CommandText = "INSERT INTO [$dbCompare].[dbo].[PathA] (FilePath,FileName,FileLength,LastWriteTime) VALUES ($insFolder,$insFile,$insLength,$insLastWrite)"
    # Execute the SQL command while also incrementing the row count for use later
    $PathArowCount = $PathArowCount + $sqlCommand.ExecuteNonQuery()
} 
write-output "$(Get-Date) - $PathArowCount rows (eg files found) inserted into table PathA"

正如你所看到的,我已经设置了输入值$insFolder$insFile$insLength$insLastWrite,这些值传递给SQL命令,因为这是SQL所期望的。
我的问题是文件/文件夹名称包含撇号/单引号,例如:
c:\Myfiles\Keith's Document.docx
我从$sqlCommand.ExecuteNonQuery()行得到以下错误:

Exception calling "ExecuteNonQuery" with "0" argument(s): Incorrect syntax near 's'.
Unclosed quotation mark after the character string')'."

Write-Host行向我展示了在这一点上$insFile确实正确地包含了整个字符串,每一端都有单引号,例如:

'Keith's Document.docx'

但是从错误SQL可能会看到传递给它的值为

'Keith'

然后用意外的参数出错:

s Document.docx'

我显然需要找到并逃离出的字符在相关的地方,我已经尝试了各种组合例如

$insFile = "`'$($file.Name).Replace("`'","\`'")`'"

我用反勾转义比较值中的单引号,并尝试在反勾上添加一个\以在字符到达SQL时转义该字符,但我尝试过的每个组合都会抛出一个PowerShell错误,所以我不确定神奇组合是什么。

dxpyg8gm

dxpyg8gm1#

tl;dr
最好使用parameterized query而不是 * 字符串插值,这既可以避免引用问题,更重要的是,可以消除SQL injection攻击的任何可能性。

$insFile = "'$($file.Name -replace "'", "''")'"

# Alternative:
$insFile = "'{0}'" -f ($file.Name -replace "'", "''")
报价注意事项:

PowerShellstring literals 的透视图:

  • "..."内部-可扩展(插值)字符串-'字符。不需要转义,可以按原样使用。
  • '...'内部-逐字字符串-嵌入的'字符,必须转义为''-然而,这里 * 不 * 涉及这样的字符串;见下文。
    T-SQL透视图(其中字符串字面量称为 * 字符串常量 *):
  • '...'嵌入在查询字符串中的字符串碰巧 * 也 * 需要将string-internal '转义为''
  • (默认情况下不支持"..."文字,并且这两种形式都不是插值)
1sbrub3j

1sbrub3j2#

非常感谢@SantiagoSquarzon为我指出了我当时甚至没有意识到的正确方向。让我的脚本使用参数化查询(一旦我学会了如何),认为这将提高安全性,并惊喜地意识到(事后看来,这是一种明显的),它也否定了转义字符的问题。
对于任何感兴趣的人,我最终得到的工作代码是

$PathArowCount=0
$filelist = Get-ChildItem -Path $localPath -File -Recurse | Select DirectoryName, Name, Length, LastWriteTime 
ForEach ($file in $filelist)
{
    $sqlCommand.CommandText = "INSERT INTO [$dbCompare].[dbo].[PathA] (FilePath,FileName,FileLength,LastWriteTime) VALUES (@Folder,@File,@Length,@LastWrite)"
    $sqlCommand.Parameters.Clear()
    $sqlCommand.Parameters.Add("@folder", $($file.DirectoryName)) | Out-Null
    $sqlCommand.Parameters.Add("@file", $($file.Name)) | Out-Null
    $sqlCommand.Parameters.Add("@length", $($file.Length)) | Out-Null
    $sqlCommand.Parameters.Add("@lastwrite", $($file.LastWriteTime)) | Out-Null
    $PathArowCount = $PathArowCount + $sqlCommand.ExecuteNonQuery()
} 
write-output "$PathArowCount rows (eg files found) inserted into table PathA"
  • 注意,从下面的注解中可以看出,这里使用的.Add(方法已被弃用,因此它可能无法在新版本中工作。已确认在Windows Server 2012(SQL 2012)和Windows Server 2012 R2(SQL 2016)上使用Powershell 5.1。*

有趣的是,在我达到这一点之前,当我还在争论转义字符时,我发现

$insFile = "`'$($file.Name).Replace("'","''")`'"

给出了一个Powershell错误,将其分为两行,如下所示

$insFileTmp = $($file.Name).Replace("'","''")
$insFile = "`'$insFileTmp`'"

工作得很好,做了我以前的事情(在知道更好之前!)寻找。

相关问题