变量不会在PowerShell中存储数据

时间:2014-11-21 05:58:34

标签: variables powershell

出于某种原因,我无法使用Get-Content cmdlet获取此变量($ SelectedDrive)来存储数据。

我创建了一个如下所示的函数:

#Allow user to select which drive to backup to and checks that the user hasn't canceled out of the Drive Selection GUI
Function SelectDrive{
ShowUSBs | Out-File $locBackupFolder\RemovableStorage.txt
Get-Content $locBackupFolder\RemovableStorage.txt | Out-GridView -OutputMode Single -Title "Choose USB Storage Device" | Out-File $locBackupFolder\Drive.txt -Encoding default
$SelectedDrive = Get-Content $locBackupFolder\Drive.txt
IF ($SelectedDrive -eq $null) {
    WarningCancel
    RemoveBackupDrive
    Exit
    } ELSE {
    $BackupDrive = $SelectedDrive.Substring(0,2)
    }
}

当我使用F5按钮在Powershell ISE中运行脚本时,它会运行,然后无法输入数据。

我回去查看它并且看不到任何问题,因为如果我使用“运行选择”逐个运行它就可以了。

整个脚本如下:

######### DEFINE FUNCTIONS #########
$locBackupFolder =  "C:\Backup"

#Check Backup Drive
IF (!(Test-Path $locBackupFolder)) {
    #C:\Backup does NOT exist
    New-Item $locBackupFolder -ItemType Directory
}

#Inform user only D:\Username will be backed up
function WarningDialog(
$MessageWarning = "!!!WARNING!!!

YOU MAY LOSE DATA IF YOU DO NOT ADHERE TO THIS MESSAGE

Only D:\$env:USERNAME will be backed up, please ensure any Data that is sitting in the root of D:\ is moved to D:\$env:USERNAME

FYI: Your desktop data is safe and will be backed up, you need only worry about data in the root of D:\ or anything you have stored in 
C:\ Drive.

FURTHER MORE Outlook will shutdown by itself through this process, please do not open it up again until everything is finished.

If you have data to move, please click Cancel now, otherwise please press OK to continue the backup procedure.

For any help, please see your IT Technicians, or call
off-site.",

$WindowTitleWarning = "Backup your data from your laptop",
[System.Windows.Forms.MessageBoxButtons]$ButtonsWarning = [System.Windows.Forms.MessageBoxButtons]::OKCancel,
[System.Windows.Forms.MessageBoxIcon]$IconWarning = [System.Windows.Forms.MessageBoxIcon]::Stop
)
{
    Add-Type -AssemblyName System.Windows.Forms
    return [System.Windows.Forms.MessageBox]::Show($MessageWarning, $WindowTitleWarning, $ButtonsWarning, $IconWarning)
}

#Checks to see if logged on user has a D:\USERNAME directory and if not, informs them to see IT for a custom backup.
Function TestUserDrive {
    IF (!(Test-Path "D:\$env:USERNAME")){
        #D:\USERNAME does NOT exist
    }
}

#Displays an instruction/how-to of how to move data from the root of D:\ to the users D:\USERNAME folder
function WarningCancel(
$MessageCancel = "INSTRUCTIONS:

You have chosen to cancel the backup script, if this is due to requiring data to be moved to inside your D:\$env:USERNAME folder, please do the following.

1. Open My Computer

2. Double click on Data (D:) to open your D:\ Drive

3. Move or Copy anything from this directory that you wish to keep, into the directory called $env:USERNAME\My Documents

4. Once this has been completed, re-run this script to commence the backup procedure again

Easy as that!

For any help, please see your IT Technicians, or call
off-site",
$WindowTitleCancel = "Backup your data from your laptop",
[System.Windows.Forms.MessageBoxButtons]$ButtonsCancel = [System.Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]$IconCancel = [System.Windows.Forms.MessageBoxIcon]::Information
)
{
    Add-Type -AssemblyName System.Windows.Forms
    return [System.Windows.Forms.MessageBox]::Show($MessageCancel, $WindowTitleCancel, $ButtonsCancel, $IconCancel)
}

#Informs the user to select the device they would like to backup to when the selection box is displayed
function SelectDevicePrompt(
$MessageSelect = "On the next screen please specify the device you would like to backup your data to.

The devices you currently have plugged in will show, please select your chosen device, and then click the OK button at the bottom right of the window.",

$WindowTitleSelect = "Backup your data from your laptop",
[System.Windows.Forms.MessageBoxButtons]$ButtonsSelect = [System.Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]$IconSelect = [System.Windows.Forms.MessageBoxIcon]::Hand
)
{
    Add-Type -AssemblyName System.Windows.Forms
    return [System.Windows.Forms.MessageBox]::Show($MessageSelect, $WindowTitleSelect, $ButtonsSelect, $IconSelect)
}

#Displays a list of all removable storage devices volume names and their allocated drive letter
Function ShowUSBs{
$USBInfo = gwmi win32_diskdrive | ?{$_.interfacetype -eq "USB"} | %{gwmi -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE AssocClass = Win32_DiskDriveToDiskPartition"} |  %{gwmi -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass = Win32_LogicalDiskToPartition"}
$USBInfo | Format-Table `
    @{Expression={$_.DeviceID};Label="Drive Letter";Width=25}, `
    @{Expression={$_.VolumeName};Label="USB Name";Width=20}
}

#Allow user to select which drive to backup to and checks that the user hasn't canceled out of the Drive Selection GUI
Function SelectDrive{
ShowUSBs | Out-File $locBackupFolder\RemovableStorage.txt
Get-Content $locBackupFolder\RemovableStorage.txt | Out-GridView -OutputMode Single -Title "Choose USB Storage Device" | Out-File $locBackupFolder\Drive.txt -Encoding default
$SelectedDrive = Get-Content $locBackupFolder\Drive.txt
IF ($SelectedDrive -eq $null) {
    WarningCancel
    RemoveBackupDrive
    Exit
    } ELSE {
    $BackupDrive = $SelectedDrive.Substring(0,2)
    }
}
$BackupDrive

#Imports list of active processes and looks for outlook process then kills it if found
function KillOutlook{
$processactive = Get-Process
IF($processactive.ProcessName -contains "Outlook") {
    Stop-Process -Name Outlook
    Start-Sleep 1
    $OLcheckagain =  Get-Process
    IF($OLcheckagain.Processname -contains "Outlook") {
        Write-Host "Outlook failed to close"
        }
    } Else {
    Write-Host "Outlook is closed"
    }
}

#Find the pst files attached to outlook and output the values to C:\Backup\PST.txt
function FindPSTs{
$outlook = New-Object -comObject Outlook.Application 
$pstloc = $outlook.Session.Stores | where { ($_.FilePath -like '*.PST') }
$pstloc.FilePath | out-file -FilePath "$locBackupFolder\PST.txt" -Encoding Default
}

#Removes C:\Backup Directory
Function RemoveBackupDrive {
    IF (Test-Path $locBackupFolder){
    Remove-Item $locBackupFolder -Force -Recurse
    }
}

#Copy data from D:\USERNAME to BackupDrive
Function CopyData {
    IF (!(Test-Path $BackupDrive)) {
    robocopy D:\$env:USERNAME $BackupDrive /MIR /COPYALL /r:03 /w:5 /MT:9
    } ELSE {
    robocopy D:\$env:USERNAME $BackupDrive /
    }
}

#Copy PST files explicitly to BackupDrive\AppData\Roaming\Email
Function CopyPST {
    KillOutlook
    Start-Sleep 1
    IF (!(Test-Path $BackupDrive\AppData\Roaming\Email)) {
    New-Item $BackupDrive\AppData\Roaming\Email -ItemType Directory
    }
    Get-Content $locBackupFolder\PST.txt | ForEach-Object {
        IF (Test-Path $_){
        Copy-Item $_ "$BackupDrive\AppData\Roaming\Email"
        }
    }
}


######### START SCRIPT #########

#Display warning to inform user that only D:\USERNAME will be backed up
$WarningAccept = WarningDialog

#If cancel is selected from WarningDialog, then display WarningCancel message pop-up giving instructions to backup data from D:\ to D:\USERNAME
IF ($WarningAccept -eq "Cancel") {
WarningCancel
RemoveBackupDrive
Exit
}

#Prompts user to select Device to backup to
SelectDevicePrompt

#Shows the selection page for the user to select the device
SelectDrive

#Find the pst files attached to outlook and output to C:\Backup\PST.txt
FindPSTs

#Inform user where their data will be backed up to
Write-Host "Your data will be backed up to $BackupDrive"

#If Outlook is found, stop its process, otherwise continue
KillOutlook

#Running backup of everything in D:\ drive using robocopy
#If this is the first time copying all data /MIR will be used, otherwise if the directory DRIVE:\USERNAME\Backup exists
#robocopy will not /MIR and will only update newly added data.
CopyData

#Copy PST files specifically
#CopyPST

如果我有任何进一步的帮助,请告诉我。

3 个答案:

答案 0 :(得分:1)

我认为你的问题只是SelectDrive函数没有返回任何内容。 $backupDrive是函数的本地函数,因此您无法从函数外部引用它。

同时将所有内容转换为文本将引入其他错误,例如您可以从网格视图中选择任何行,包括标题和顶部和底部的空白行。一些更好的代码可能是:

Function GetUSBs{
    $USBInfo = gwmi win32_diskdrive |
        ?{$_.interfacetype -eq "USB"} |
        %{gwmi -Query "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=`"$($_.DeviceID.replace('\','\\'))`"} WHERE AssocClass = Win32_DiskDriveToDiskPartition"} |
        %{gwmi -Query "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=`"$($_.DeviceID)`"} WHERE AssocClass = Win32_LogicalDiskToPartition"}
    Write-Output $USBInfo | select @{n="Drive Letter";e={$_.DeviceID}},@{n="USB Name";e={$_.VolumeName}}
}

Function SelectDrive{
    $usbs = GetUSBs 
    $usbs |  Format-Table -AutoSize | Out-File $locBackupFolder\RemovableStorage.txt

    $selectedDrive = $usbs | Out-GridView -OutputMode Single -Title "Choose USB Storage Device"
    $selectedDrive | Out-File $locBackupFolder\Drive.txt -Encoding default

    if ($SelectedDrive -eq $null) {
        WarningCancel
        RemoveBackupDrive
        exit
    } else {
    write-output $selectedDrive.'Drive Letter'.Substring(0,2)
    }
}
$backupDrive = SelectDrive

此版本仍会写入您的两个文件,但它不会读取其中任何一个文件,而是在内部保留对象。这意味着您可以在网格视图中获得更好的显示,因为使用了网格自己的列标题,并且您没有获得任何您不想要的可选行。

答案 1 :(得分:0)

您的脚本中有很多内容,我自己通过它进行调试,发现这些行导致了问题

ShowUSBs | Out-File $locBackupFolder\RemovableStorage.txt
Get-Content $locBackupFolder\RemovableStorage.txt | Out-GridView -OutputMode Single -Title "Choose USB Storage Device" | Out-File $locBackupFolder\Drive.txt -Encoding default
$SelectedDrive = Get-Content "$locBackupFolder\Drive.txt"

您需要删除此代码块:

Out-File $locBackupFolder\Drive.txt -Encoding default

它正在创建一个新的空白Drive.txt文件,之后,在该空文件上调用Get-Content

因此,它获取空白文件的内容,这将始终导致$SelectedDrive变量设置为null。

我删除了此块后,我的Drive.txt文件中添加了内容,$SelectedDrive变量按预期设置为文件内容

答案 2 :(得分:0)

这种结构不会像您期望的那样起作用:

Get-Content file | Out-GridView | Out-File otherfile

Out-GridView不会将数据传回管道以进行进一步处理。如果您在网格视图中都需要数据,则需要在Out-GridView之前使用Tee-Object cmdlet:

Get-Content file | Tee-Object otherfile | Out-GridView

在你的情况下:

Get-Content "$locBackupFolder\RemovableStorage.txt" |
  Tee-Object "$locBackupFolder\Drive.txt" |
  Out-GridView -OutputMode Single -Title 'Choose USB Storage Device'

如果您希望用户选择特定的项目,Out-GridView根本不会工作,因为输出只(因此动词Out)。您需要form with a list box才能允许用户选择项目:

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$frm = New-Object Windows.Forms.Form

$btn = New-Object Windows.Forms.Button
$btn.Location = New-Object Drawing.Size(10,220)
$btn.Size = New-Object Drawing.Size(75,23)
$btn.Text = "OK"
$btn.Add_Click({$frm.Close()})
$frm.Controls.Add($btn)

$list = New-Object Windows.Forms.ListBox
$list.Location = New-Object Drawing.Size(10,10)
$list.Size = New-Object Drawing.Size(260,40)
$list.Height = 200
Get-Content "$locBackupFolder\RemovableStorage.txt" | % {
  $list.Items.Add($_)
} | Out-Null
$frm.Controls.Add($list)

$frm.Add_Shown({$frm.Activate()})
[void]$frm.ShowDialog()

$SelectedDrive = $list.SelectedItem