Powershell proper way to pass object to function - powershell

I am trying to list all msi files found in a given dir to a file, then extract those msi files. I am trying to use the $_ in the foreach-object to pass the path however it seems to be interpreting it as a literal, instead of passing the path. I thought the $_ would pass the object, which in this case would be the filepath, but it doesnt seem to be functioning that way. What is the proper way to pass the found filepaths to the Export-MsiContents function?
Function Export-MsiContents
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true, Position=0)]
[ValidateNotNullOrEmpty()]
[ValidateScript({Test-Path $_})]
[ValidateScript({$_.EndsWith(".msi")})]
[String] $MsiPath,
[Parameter(Mandatory=$false, Position=1)]
[String] $TargetDirectory
)
if(-not($TargetDirectory))
{
$currentDir = [System.IO.Path]::GetDirectoryName($MsiPath)
Write-Warning "A target directory is not specified. The contents of the MSI will be extracted to the location, $currentDir\Temp"
$TargetDirectory = Join-Path $currentDir "Temp"
}
$MsiPath = Resolve-Path $MsiPath
Write-Verbose "Extracting the contents of $MsiPath to $TargetDirectory"
Start-Process "MSIEXEC" -ArgumentList "/a $MsiPath /qn TARGETDIR=$TargetDirectory" -Wait -NoNewWindow
}
$Dir = get-childitem d:\temp\test -recurse
$List = $Dir | where {$_.extension -eq ".msi"}
$List | format-table fullname | out-file d:\temp\test\msilist.txt
$List | ForEach-Object {Export-MsiContents -MsiPath $_}

It looks like you are trying to specify a childitem object as the $MsiPath which should be a string.
So you would need to specify which value in that object to use as $MsiPath. In this case it looks like you would like to pass the fullname.
Try this:
$List | ForEach-Object {Export-MsiContents -MsiPath $_.fullname}

Related

How to write an advanced function to select path and sort files?

function Sort-Size {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string] $Name
[Parameter(ValueFromPipeline = $true)]
$Path,
$Lenght
)
begin {
$Lenght = #()
}
process {
$Path = Get-ChildItem -recurse -File $Name
}
end {
$Path | Select-Object FullName, #{Name='FileSizeInKb';Expression={$_.Length/1KB}} | Sort-Object -Property FileSizeInKb | Format-Table -AutoSize
}
}
Sort-Size -Name {Test-Path -Name "C:\"}
This code only sorts in one folder, how to make it sort in different folders?
For this you don't need parameters Name and Length.
Instead, I would add an optional file pattern to look for and add a switch whether or not you want the function to recurse through the various subfolders.
Also, I would not have it return the DISPLAY ONLY output of Format-Table, but the sorted list of files itself. Then when caling the function you can decide if you want to pipe that to Format-* or do something else with the data.
Something like below:
function Sort-Size {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[ValidateScript({ $_ | Test-Path -PathType Container })]
[Alias('FullName')]
[string]$Path,
[string]$Pattern = '*.*',
[switch]$Recurse
)
Write-Verbose "Searching files in '$Path' with pattern '$Pattern'"
Get-ChildItem -Path $Path -File -Filter $Pattern -Recurse:$Recurse |
Select-Object Name, DirectoryName, #{Name='FileSizeInKb';Expression={[Math]::Round($_.Length/1KB, 2)}} |
Sort-Object FileSizeInKb
}
Sort-Size -Path "C:\Users\user" | Format-Table -Property Name,FileSizeInKb -AutoSize
function Sort-Size {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline = $true)]
$Path,
$Lenght,
$Name
)
begin {
$Lenght = #()
}
process {
$Path = Get-ChildItem -recurse -File "$Name"
}
end {
$Path | Select-Object FullName, #{Name='FileSizeInKb';Expression={$_.Length/1KB}} | Sort-Object -Property FileSizeInKb | Format-Table -AutoSize
}
}
#Sort-Size -Path "C:\Users"
Sort-Size -Name "C:\Users\user"
#Get-Help "Sort-size"

Output list of files to log

I'm writing a script where I'm trying to output the results from a Get-ChildItem command to a log file. The script below is simplified to show the issue I'm having. For example, the WriteLog function is used several times in the actual Script. The file listing is not the only thing to be added to the log file.
The snippet below writes a long run-on line of all full filenames to the log.
$FilePath = "G:\Folder"
$LogPathName = "G:\Folder\TestLog.log"
Function WriteLog {
Param ([string]$LogString)
$Stamp = Get-Date
$LogMessage = "$Stamp - $LogString"
Add-Content $LogPathName -value $LogMessage
}
$FileList = Get-ChildItem –Path $FilePath -include ('*.csv', '*.xlsx')
writelog $FileList
I want each filename to begin on a new line--like a list. How can I do this?
Leaving your function WriteLog as is, the workaround is to iterate over each element of the array returned by Get-ChildItem so that the function appends to the file line-by-line:
foreach($item in $FileList) {
WriteLog $item
}
However a more elegant way of approaching this would be to leverage ValueFromPipeline, then you could simply pipe Get-ChildItem into your function and let the process block handle each element. You can also add a -PassThru switch to it in case you also want the same object be returned as output for later manipulation. And lastly, it may be worth adding a new -Path parameter to make it reusable.
function Write-Log {
param(
[Parameter(ValueFromPipeline, Mandatory)]
[object] $InputObject,
[parameter(Mandatory)]
[string] $Path,
[parameter()]
[switch] $PassThru
)
begin { $sb = [System.Text.StringBuilder]::new() }
process {
$sb = $sb.AppendLine("$(Get-Date) - $InputObject")
if($PassThru.IsPresent) { $InputObject }
}
end { Add-Content $Path -Value $sb.ToString() -NoNewline }
}
$FileList = Get-ChildItem -Path .\* -Include '*.csv', '*.xlsx' |
Write-Log -Path "G:\Folder\TestLog.log" -PassThru

Searching with Get-ChildItem over the network

I'm having an issue while searching with PowerShell over the network; program get stuck while executing Get-ChildItem.
# creating search string
$date = "*2018-01-10*"
$format = ".avi"
$toSearch = $date + $format
echo $toSearch
# Verifying A: drive to be disconnected
net use /d A: /y
# connecting Network drive to local drive A:
if (Test-Connection -ComputerName PROC-033-SV -Quiet) {net use A: \\PROC-033-SV\c$}
# getting list of directories to search on
$userList = Get-ChildItem -Path A:\users\*.* -Directory
# verifying list of directories prior to search
echo $userList
# searching through Network, on A:\Users\ subdirectories for $toSearch variable
Get-ChildItem -Path $userList -Include $toSearch -Recurse -Force
# *** HERE's where the program get stuck, it nevers stop searching
# it nevers reach pause
pause
Does anyone know why Get-ChildItem keeps looping and it never stops?
I'm using PS v4 no -Depth option available for -Recurse parameter; I'm suspecting that might be the issue.
If you want to limit recursion depth in PowerShell v4 and earlier you could wrap Get-ChildItem in a custom function, e.g. like this:
function Get-ChildItemRecursive {
[CmdletBinding()]
Param(
[Parameter(
Mandatory=$false,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true
)]
[string[]]$Path = $PWD.Path,
[Paramter(Mandatory=$false)]
[string]$Filter = '*.*',
[Parameter(Mandatory=$false)]
[int]$Depth = 0
)
Process {
Get-ChildItem -Path $Path -Filter $Filter
if ($Depth -gt 0) {
Get-ChildItem -Path $Path |
Where-Object { $_.PSIsContainer } |
Get-ChildItemRecursive -Filter $Filter -Depth ($Depth - 1)
}
}
}

Replace command in powershell is deleting the whole content

I am new to powershell. I create a powershell script which need to search a string in the path provided in parameters and replace that string. But actually it is replacing entire file content with new string.
I am using Powershell in Windows 10 OS.
Code:
param(
[Parameter(Mandatory=$true, ParameterSetName="Path", Position=0,HelpMessage='Data folder Path')]
[string] $Path,
[Parameter(Mandatory=$true, HelpMessage='Input the string to be replaced')]
[string] $Input,
[Parameter(Mandatory=$true,HelpMessage='Input the new string that need to be replaced')]
[string] $Replace
)
$a = Test-Path $Path
IF ($a -eq $True) {Write-Host "Path Exists"} ELSE {Write-Host "Path Doesnot exits"}
$configFiles = Get-ChildItem -Path $Path -include *.pro, *.rux -recurse
$Append = join-path -path $path \*
$b = test-path $Append -include *.pro, *.rux
If($b -eq $True) {
foreach ($file in $configFiles)
{
(Get-Content $file.PSPath) |
Foreach-Object { $_ -replace [regex]::Escape($Input), $Replace } |
Set-Content $file.PSPath
}
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("Operation Completed",0,"Done",0x0)
}
As best I can read this without directly reproducing it, this is where it goes wrong:
(get-content $file.pspath) gets the entire content of the file, not its name.
Your "foreach" then regexes every line in the file, and finally "set-content" replaces the contents of the file, not its path.
If you want to change the name of a file, you are looking for Rename-Item, not Set-Content. If you want the name of a file $file.Name will do, you don't need Get-Content, which will ... get its content :)
This should be a working solution.
Param(
[Parameter(Mandatory,
ParameterSetName='Path',
Position=0,
HelpMessage='Data folder Path')]
[String]
$Path,
[Parameter(Mandatory,
HelpMessage='Input the string to be replaced')]
[String]
$StringToReplace,
[Parameter(Mandatory,
HelpMessage='Input the new string that need to be replaced')]
[String]
$ReplacementString
)
If (!(Test-Path $Path)) {
Write-Host 'Path does not exist'
Return
}
Get-ChildItem -Path $Path -Include *.pro,*.rux -Recurse |
? { $_.Name -like "*$StringToReplace*" } |
% { Rename-Item $_ $($ReplacementString+$_.Extension) }
(New-Object -ComObject Wscript.Shell).Popup("Operation Completed",0,"Done",0x0)

Passing path parameter into power shell script using batch file

I would like to pass in a path variable --> param ([string]$w) into another parameter that store the path in power shell script. Then this power shell script is called using another batch file. I failed to pass in the $ which is the folder name that completes the full path . Please suggest me solution on this
$FilePath = "C:\Root\Main\Subfolder\param ([string]$w)\Table\"
$FileExists = Test-Path $FilePath
If ($FileExists -eq $True)
{ Get-ChildItem -path C:\Root\Main\Subfolder\param ([string]$w)\Table\ -Recurse -Filter *.sql |
` Sort-Object -Property DirectoryName -Desc | `
Foreach-Object -Process {$_.FullName } |ForEach-Object {sqlcmd -i $_}
}
Else {Write-Host "No file at this location"}
This is my batch file command line
PowerShell.Exe -File C:\Users\AZ\Desktop\PowerShell\untitled7.ps1 "Payment"
Try putting this at the top of your script:
param(
[Parameter(
Mandatory=$true,
Position=0,
HelpMessage='Set path variable')]
[string] $w
)
Replace:
param ([string]$w)
with:
$w
where it appears in your script.