Retrieve parameters from file - Windows PowerShell - windows

I am writing a super-easy script in PowerShell. The target of this script is to read a list of server names from a txt file and a command block from another txt file. The result of the operation shold be a third txt file containing the information.
Here some code:
cls
$usr = Read-Host "Please insert username, you'll be asked for password later"
$path = Read-Host "Insert a valid path for ServerList.txt file"
$serverList = Get-Content -Path $path | Out-String
$path = Read-Host "Insert a valid path fom Command.txt file"
$commandBlock = Get-Content -Path $path | Out-String
echo "Command: " $commandBlock "will be executed on " $serverList
echo "Press CTRL+Z to abort or"
pause
Invoke-Command -ComputerName $serverList -ScriptBlock { $commandBlock } -credential $usr
Serverlist.txt is a plain text containing something like "server1,server2,server3" and command.txt contain only this "Get-WmiObject Win32_BIOS | Select-Object SerialNumber"
Why the error is Invoke-Command : One or more computer names are not valid. If you are trying to pass a URI, use the -ConnectionUri parameter, or pass URI objects
instead of strings. ?
I even tried to substitute $serverlist with $serverlist.toString() but it's not working. I read somewhere that in this case $serverlist is an Array, how do I do to make everything work?
Consider that https://technet.microsoft.com/en-us/library/hh849719.aspx Invoke-Commands work with "server1,server2,server3" format if you put the string via console.

Your $serverList isn't a list, it's a single string of server1,server2 etc. To make it into an array, you can use -split to split the string by commas.
$serverList = Get-Content -Path $path | Out-String
$serverList = $serverList -split ","

For further understanding of why this doesn't work as you expect, please see the parsing and command syntax help files:
Get-Help about_Parsing
Get-Help about_Command_Syntax
$serverlist
When your text file contains the line server1,server2,server3, this command:
Get-Content -Path .\file.txt | Out-String
Just results in the string server1,server2,server3 and a newline - that's not a valid hostname.
Either format your text file like this (Get-Content automatically splits on line breaks):
server1
server2
server3
or split the string(s) from the file yourself:
$Serverlist = Get-Content -Path $Path | ForEach-Object { $_ -split "," }
$commandblock
For the command block part to work, you can't just drop a string into a ScriptBlock and expect it to execute - you need to recreate it as executable code:
$Code = Get-Content -Path $path -Raw
$CommandBlock = [scriptblock]::Create($Code)
# Now you can do this
Invoke-Command -ScriptBlock $CommandBlock

Related

How to display PowerShell results horizontally instead of vertically?

For example, you specified a variable as shown below.
$data1 = get-psdrive | where-object {$_.name -like 'c'} | select -expandproperty used
$data2 = get-psdrive | where-object {$_.name -like 'c'} | select -expandproperty free
echo $data1,data2
The output is vertical.
$data1
$data2
I used write-host -nonewline to display output horizontally, but the command does not export to txt
write-host $data1 -nonewline; write-host $data2 -nonewline >> c:\test.txt
How can I display horizontally and export in txt?
How about converting to json (or csv)? Note that ">>" can mix encodings, but add-content doesn't.
get-psdrive c | select used, free | ConvertTo-Json -Compress |
add-content test.txt
get-content test.txt
{"Used":217365741568,"Free":21004943360}
Or just join them. Too bad select -expand doesn't work with multiple properties.
psdrive c | % { ($_.used,$_.free) -join ',' }
217382371328,20988313600
You can't use Write-Host for this. Even if you change to Write-Host -NoNewLine it'll still never work because Write-Host is intended for directly writing into the screen and can't be redirected unless you use PowerShell 5+ and redirect the stream number 6 (Information stream)
Write-Host -NoNewLine $data1,$data2 6>output.txt
in which case it doesn't print out to string of course. In short Write-Host in PowerShell 5+ doesn't write to stdout but the Information stream
The real solution to writing to screen with the ability to redirect to file or pipe to another command is to use Write-Output (which echo is an alias to), and simply use a single string to write to a single line
Write-Output "$data1,$data2"
echo "$data1,$data2"
echo ($data1 + "," + $data2)
See Write-Host Considered Harmful for more details
Use a double quote string with your variables: Write-Host “$data1,$data2”
Read more https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_quoting_rules?view=powershell-7.1

process multiple CSV file and delete rows in a single column which has double semi colon characters using powershell

consider I have a below CSV file.
input:
ID;ITEM_ID;STATUS;
001;;RELEASED;
002;36530;RELEASED;
003;86246;RELEASED;
004;;RELEASED;
I want to remove the row that has ;; (ITEM_ID) missing and save it.I tried doing it on one sample file and it worked as expected.
Import-Csv -Path ".\TestFile.CSV" | where {$_.ITEM_ID -ne ""} | Export-Csv -Path ".\TestFile-temp.CSV" -NoTypeInformation
Remove-Item -Path '.\TestDir\TestFile.csv'
Rename-Item -Path '.\TestDir\TestFile-temp.csv' -NewName 'TestFile.csv'
output:
ID;ITEM_ID;STATUS;
002;36530;RELEASED;
003;86246;RELEASED;
The challenge is, i have multiple csv files and it doesn't has value in different columns, but in single column when i opened in excel file.
so it's not taking the condition < where {$_.ITEM_ID -ne ""} >.
Now i have to search/parse each row of each csv file, search special character (;;) in that row and delete the line and save the file.
i am good at shell scripting but, i am very new to powershell scripting. can anybody please help me to get the logic here or use other cmdlet that can do the job?
$fileDirectory = "C:\Users\Administrator\Documents\check";
foreach($file in Get-ChildItem $fileDirectory)
{
$csvFileToCheck = Import-Csv -Path $fileDirectory\$file
$noDoubleSemiComma = foreach($line in $csvFileToCheck)
{
if(Select-String << i want the logic here>>)
{
$line
}
}
$noDoubleSemiComma | Export-Csv -Path $fileDirectory\tmp.csv -NoTypeInformation
Remove-Item -Path $fileDirectory\$file
Rename-Item -Path $fileDirectory\tmp.csv -NewName $file
}
As commented, you need to add parameter -Delimiter ';' to the cmdlet otherwise a comma is used to parse the fields in the CSV.
As I understand, you also want to remove the quotes Export-Csv outputs around all fields and headers and for PowerShell version 7 you have the option to use parameter -UseQuotes AsNeeded.
As this is not available for version 5.1, I made a function ConvertTo-CsvNoQuotes some time ago to remove the quotes in a safe way. (simply replacing them all with an empty string is dangerous, because sometimes values do need quotes)
Copy that function into your script at the top, then below that, your code could be simplified like this:
$fileDirectory = "C:\Users\Administrator\Documents\check"
Get-ChildItem -Path $fileDirectory -Filter '*.csv' -File | ForEach-Object {
# for better readability store the full path of the file in a variable
$filePath = $_.FullName
(Import-Csv -Path $filePath -Delimiter ';') | ConvertTo-CsvNoQuotes -Delimiter ';' | Set-Content $filePath -Force
Write-Host "File '$filePath' modified"
}
After all helpful suggestion, i finally nailed it down. AS my power-shell version was 5.1 , i had to use logic for trimming double quotes after export-csv. Powershell version 7 and later has -UseQuotes that could have solve that too.
Hope this help others.
$fileDirectory = "C:\Users\Administrator\Documents\check";
foreach($file in Get-ChildItem $fileDirectory)
{
Import-Csv -Path $fileDirectory\$file -Delimiter ';' | where {$_..ITEM_ID -ne ""} | Export-Csv -Path $fileDirectory\temp.csv -Delimiter ';' -NoTypeInformation
$Test = Get-Content $fileDirectory\temp.csv
$Test.Replace('";"',";").TrimStart('"').TrimEnd('"') | Out-File $fileDirectory\temp.csv -Force -Confirm:$false
Remove-Item -Path $fileDirectory\$file
Rename-Item -Path $fileDirectory\temp.csv -NewName $file
Write-Output "$file file modified."
}
Any suggestion to trim down number of lines of code is welcomed.

Pattern as a input in powershell

I am trying a script that could compress and delete folders which is in 'n' sublevel folders.
For example the below script could do the job for 3 sublevel folders.
$path = Read-Host "Enter the path"
$directory = $path +"\*\*\*"
Add-Type -AssemblyName System.IO.Compression.FileSystem
$folders = Get-ChildItem $directory -recurse | Where-Object {$_.PSIsContainer -eq $true} | Select-object -ExpandProperty FullName
foreach ($folder in $folders) {
Write-Verbose "Archiving $archive"
$archive = $folder + '.zip'
[System.IO.Compression.ZipFile]::CreateFromDirectory($folder, $archive, 'Optimal', $True)
Remove-Item $folder -recurse -force -Verbose
}
The script is working fine...My doubt is, how to input the sublevel as a input value?
In the above script I am giving the path as a input...Likewise, I wish to input the sublevel also as a input value.
For example: Enter the level:3 (This should assume the pattern like (bs* bs* bs*)
or 4 (bs* bs* bs* bs*)
Note : I had mentioned \ as bs. Because if I mention the pattern as in script, its not visible in the preview.
Any help?
PowerShell allows you to replicate strings with its * operator:
PS> $numLevels = 3; $path = 'C:\path\to'; $path + ('\*' * $numLevels)
C:\path\to\*\*\*

Replacing words in powershell using a variable

i want to write a script that takes inputfrom the user and changes a word in a file to what the user entered.
saw some people doing it like this:
(Get-Content c:\temp\test.txt).replace('word1', 'word2') | Set-Content c:\temp\test.txt
the problem is that i want to replace a word with a variable, so when i put it between the commas it wont work.
i want it to be something like that:
$word = read-host " please enter a word"
(Get-Content c:\temp\test.txt).replace('oldtext', '$word') | Set-Content c:\temp\test.txt
is there any way to do that?
UPDATE:
tried it like this:
$path = "C:\Users\tc98868\Desktop\dsp.json"
$word = read-host "please enter a word"
(Get-Content $path).replace('dsp.tar.gz', $word) | Set-Content $path
and it still doesnt work.
Remove the single quote from the $word
PowerShell not expanding variables inside a single quote, it threat is as a string
$word = read-host " please enter a word"
(Get-Content c:\temp\test.txt).replace('oldtext', $word) | Set-Content c:\temp\test.txt
For PS Version 2:
(Get-Content c:\temp\test.txt) -replace 'oldtext', $word | Set-Content c:\temp\test.txt

What's wrong with my Powershell script?

I don't understand this error message I'm receiving when I try to run my powershell script. The purpose is to copy a .bat file into the main win 7 startup folder on a series of machines.
And the script I am running.
$ServerList = Get-Content "C:\ServersList.txt" #Change this to location of servers list
$SourceFileLocation = "C:\firefox_issue.bat" #For example: D:\FoldertoCopy\ or D:\file.txt
$Destination = "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup" #Example: C$\temp
foreach ($_ in $ServerList)
{Copy-Item $SourceFileLocation -Destination \\$_\$Destination -Recurse -PassThru}
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Write-Host
Write-Host "A"
Write-Host "B"
Write-Host "C"
Because your location is getting set to:
\\SERVERNAME\C:\ProgramData...
and it should be:
\\SERVERNAME\C$\ProgamData...
Your destination needs to be:
$Destination = 'C$\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup'
And your loop should be:
foreach($server in $serverList) {
Copy-Item $SourceFileLocation -Destination "\\$server\$Destination" -Recurse
}
You should probably avoid explicitly using $_ as a variable name as $_ is a special variable for accessing an object in the pipeline.
Did you read the comment behind the $Destination line?
This is a UNC path.
\\server1\c:\programdata\ is not a valid UNC-path. Try:
$Destination = "C$\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"
Also, $_ is a reserved variable for pipeline input, so you need to change it, like:
foreach ($server in $ServerList)
{Copy-Item $SourceFileLocation -Destination \\$server\$Destination -Recurse -PassThru}

Resources