使用 PowerShell 我想捕获用户输入,将输入与 Excel 电子表格中的数据进行比较,并将相应单元格中的数据写入变量。我对 PowerShell 相当陌生,似乎无法弄清楚这一点。例如:提示用户输入商店编号,他们输入“123”。然后将输入与 A 列中的数据进行比较。捕获相应单元格中的数据并将其写入变量,例如 $GoLiveDate。
任何帮助将不胜感激。
使用 PowerShell 我想捕获用户输入,将输入与 Excel 电子表格中的数据进行比较,并将相应单元格中的数据写入变量。我对 PowerShell 相当陌生,似乎无法弄清楚这一点。例如:提示用户输入商店编号,他们输入“123”。然后将输入与 A 列中的数据进行比较。捕获相应单元格中的数据并将其写入变量,例如 $GoLiveDate。
任何帮助将不胜感激。
用户输入可以这样读取:
$num = Read-Host "Store number"
Excel 可以这样处理:
$xl = New-Object -COM "Excel.Application"
$xl.Visible = $true
$wb = $xl.Workbooks.Open("C:\path\to\your.xlsx")
$ws = $wb.Sheets.Item(1)
在一个列中查找一个值并将另一列中的相应值分配给一个变量可以这样完成:
for ($i = 1; $i -le 3; $i++) {
if ( $ws.Cells.Item($i, 1).Value -eq $num ) {
$GoLiveDate = $ws.Cells.Item($i, 2).Value
break
}
}
完成后不要忘记清理:
$wb.Close()
$xl.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($xl)
我发现最好使用 OleDB 连接与 Excel 进行交互。它比 COM 互操作更快,并且比 import-csv 更不容易出错。您可以准备一组 psobject(一个 psobject 是一行,每个属性对应一列)以匹配所需的目标网格并将其插入 Excel 文件。同样,您可以插入 DataTable 而不是 PSObject 集合,但除非您从某个数据源检索数据开始,否则 PSObject 集合方式通常更容易。
这是我用于将 psobject 集合写入 Excel 的函数:
function insert-OLEDBData ($file,$sheet,$ocol) {
{
"xlsb$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0;HDR=YES;IMEX=1`";"}
"xlsx$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0 Xml;HDR=YES;IMEX=1`";"}
}
$OLEDBCon = New-Object System.Data.OleDb.OleDbConnection($cs)
$hdr = $oCol|gm -MemberType NoteProperty|%{$_.name}
$names = '[' + ($hdr-join"],[") + ']'
$vals = (@("?")*([array]$hdr).length)-join','
$sql = "insert into [$sheet`$] ($names) values ($vals)"
$sqlCmd = New-Object system.Data.OleDb.OleDbCommand($sql)
$sqlCmd.connection = $oledbcon
$cpary = @($null)*([array]$hdr).length
$i=0
[array]$hdr|%{([array]$cpary)[$i] = $sqlCmd.parameters.add($_,"VarChar",255);$i++}
$oledbcon.open()
for ($i=0;$i-lt([array]$ocol).length;$i++)
{
for ($k=0;$k-lt([array]$hdr).length;$k++)
{
([array]$cpary)[$k].value = ([array]$oCol)[$i].(([array]$hdr)[$k])
}
$res = $sqlCmd.ExecuteNonQuery()
}
$OLEDBCon.close()
}
这似乎不再起作用了。我发誓它曾经是,但也许 O365 的更新杀死了它?或者我上次在 Win 7 上使用它,并且早就搬到了 Win 10:
$GoLiveDate = $ws.Cells.Item($i, 2).Value
我仍然可以使用 .Value 写入单元格,但不能将其读入变量。而不是单元格的内容,它返回:“变体值(变体){get} {set}”
但是经过一番挖掘,我发现这确实可以将单元格读入变量:
$GoLiveDate = $ws.Cells.Item($i, 2).Text
关于下一个问题/评论 squishy79 询问慢速和后续 OleDB 解决方案,我似乎也无法让它们在现代操作系统中工作,但我自己的性能技巧是让我的所有 Excel PowerShell 脚本写入制表符分隔的 .txt 文件,如下所示:
Add-Content -Path "C:\FileName.txt" -Value $Header1`t$Header2`t$Header3...
Add-Content -Path "C:\FileName.txt" -Value $Data1`t$Data2`t$Data3...
Add-Content -Path "C:\FileName.txt" -Value $Data4`t$Data5`t$Data6...
然后在完成写入所有数据后,使用非常慢的 Com“Excel.Application”打开 .txt 文件,然后进行格式化,然后保存为 .xlsx(参见 SaveAs 的评论):
Function OpenInExcelFormatSaveAsXlsx
{
Param ($FilePath)
If (Test-Path $FilePath)
{
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $true
$Workbook = $Excel.Workbooks.Open($FilePath)
$Sheet = $Workbook.ActiveSheet
$UsedRange = $Sheet.UsedRange
$RowMax = ($Sheet.UsedRange.Rows).count
$ColMax = ($Sheet.UsedRange.Columns).count
# This code gets the Alpha character for Columns, even for AA AB, etc.
For ($Col = 1; $Col -le $ColMax; $Col++)
{
$Asc = ""
$Asc1 = ""
$Asc2 = ""
If ($Col -lt 27)
{
$Asc = ([char]($Col + 64))
Write-Host "Asc: $Asc"
}
Else
{
$First = [math]::truncate($Col / 26)
$Second = $Col - ($First * 26)
If ($Second -eq 0)
{
$First = ($First - 1)
$Second = 26
}
$Asc1 = ([char][int]($First + 64))
$Asc2 = ([char][int]($Second + 64))
$Asc = "$Asc1$Asc2"
}
}
Write-Host "Col: $Col"
Write-Host "Asc + 1: $Asc" + "1"
$Range = $Sheet.Range("a1", "$Asc" + "1")
$Range.Select() | Out-Null
$Range.Font.Bold = $true
$Range.Borders.Item(9).LineStyle = 1
$Range.Borders.Item(9).Weight = 2
$UsedRange = $Sheet.UsedRange
$UsedRange.EntireColumn.AutoFit() | Out-Null
$SavePath = $FilePath.Replace(".txt", ".xlsx")
# I found scant documentation, but you need a file format 51 to save a .txt file as .xlsx
$Workbook.SaveAs($SavePath, 51)
$Workbook.Close
$Excel.Quit()
}
Else
{
Write-Host "File Not Found: $FilePath"
}
}
$TextFilePath = "C:\ITUtilities\MyTabDelimitedTextFile.txt"
OpenInExcelFormatSaveAsXlsx -FilePath $TextFilePath
如果您不关心格式,您可以在 Excel 中按原样打开制表符分隔的 .txt 文件。当然,这对于将数据插入现有 Excel 电子表格不是很好,除非您可以让脚本在每次插入时重写整个工作表。在大多数情况下,它的运行速度仍然比使用 COM 快得多。
我找到了这个,以及叶夫根尼的回答。我必须对上述功能进行一些小改动才能使其正常工作。最值得注意的是输入数组中 NULL 或空值的处理。这是 Yevgeniy 的代码,有一些小的改动:
function insert-OLEDBData {
PARAM (
[Parameter(Mandatory=$True,Position=1)]
[string]$file,
[Parameter(Mandatory=$True,Position=2)]
[string]$sheet,
[Parameter(Mandatory=$True,Position=3)]
[array]$ocol
)
$cs = Switch -regex ($file)
{
"xlsb$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0;HDR=YES`";"}
"xlsx$"
{"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=`"$File`";Extended Properties=`"Excel 12.0 Xml;HDR=YES`";"}
}
$OLEDBCon = New-Object System.Data.OleDb.OleDbConnection($cs)
$hdr = $oCol | Get-Member -MemberType NoteProperty,Property | ForEach-Object {$_.name}
$names = '[' + ($hdr -join "],[") + ']'
$vals = (@("?")*([array]$hdr).length) -join ','
$sql = "insert into [$sheet`$] ($names) values ($vals)"
$sqlCmd = New-Object system.Data.OleDb.OleDbCommand($sql)
$sqlCmd.connection = $oledbcon
$cpary = @($null)*([array]$hdr).length
$i=0
[array]$hdr|%{([array]$cpary)[$i] = $sqlCmd.parameters.add($_,"VarChar",255);$i++}
$oledbcon.open()
for ($i=0;$i -lt ([array]$ocol).length;$i++)
{
for ($k=0;$k -lt ([array]$hdr).length;$k++)
{
IF (([array]$oCol)[$i].(([array]$hdr)[$k]) -notlike "") {
([array]$cpary)[$k].value = ([array]$oCol)[$i].(([array]$hdr)[$k])
} ELSE {
([array]$cpary)[$k].value = ""
}
}
$res = $sqlCmd.ExecuteNonQuery()
}
$OLEDBCon.close()
}