What's wrong with my Powershell script? - windows

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}

Related

Powershell - extract specific items from zip then send a message (loop with message box)

I'm trying to achieve the following with this powershell script.
Copy any .zip file from folder dropfilehere to folder IN.
For each .zip file in folder "IN" open the zip file, find only the .csv file.
When .csv file is found, extract it to $dst under name DB.csv (overwrite old file).
Empty contents of folders "dropfilehere" and "IN"
Finally, when all the above is done, create a popup box with a message to the user using wscriptshell -
This is the issue. When the message is sent, the user gets 10+ popup boxes or an endless loop of them.
In the background i see cmd.exe and conhost.exe processes appearing as each popup box gets created.
I use a batch file to call the powershell script.
Powershell.exe -ExecutionPolicy Bypass -File C:\pathtoscript\call.ps1
exit
The script is:
$dst = "C:\Testing\DB"
Copy-item -Path "C:\Users\user\dropfilehere\*.zip" -destination "C:\Testing\Other\In" -Force
Foreach ($zipfile in (Get-ChildItem "C:\Testing\Other\In\*.zip" -Recurse)) {
Add-Type -Assembly System.IO.Compression.FileSystem
$zipFile = [IO.Compression.ZipFile]::OpenRead($zipfile)
$zipFile.Entries | where {$_.Name -like '*.csv'} | foreach {$FileName = $_.Name
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$dst\DB.csv", $true)}
$zipFile.Dispose()
Remove-Item "C:\Testing\Other\In\*" -Recurse -Force
Remove-Item "C:\Users\user\dropfilehere\*" -Recurse -Force
$org="Name of Org"
$timeout = 60 # in seconds
$ws = New-Object -ComObject "Wscript.Shell"
$intButton = $ws.Popup("A new update message here`n
Another message here.",$timeout,$org, 0)
}
exit
There is code inside your foreach loop that should be placed after it, as shown below (properly indenting your code would have made that more obvious):
Add-Type -Assembly System.IO.Compression.FileSystem
$dst = "C:\Testing\DB"
Copy-item -Path "C:\Users\user\dropfilehere\*.zip" -destination "C:\Testing\Other\In" -Force
# Process all files.
foreach ($zipfile in (Get-ChildItem "C:\Testing\Other\In\*.zip" -Recurse)) {
$zipFile = [IO.Compression.ZipFile]::OpenRead($zipfile)
$zipFile.Entries |
Where-Object { $_.Name -like '*.csv' } |
ForEach-Object {
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, "$dst\DB.csv", $true)
}
$zipFile.Dispose()
}
# Remove the folders containing the original *.zip files.
Remove-Item "C:\Testing\Other\In\*" -Recurse -Force
Remove-Item "C:\Users\user\dropfilehere\*" -Recurse -Force
# Show a message box.
$org = "Name of Org"
$timeout = 60 # in seconds
$ws = New-Object -ComObject "Wscript.Shell"
$intButton = $ws.Popup("A new update message here`nAnother message here.", $timeout, $org, 0)

Removing trailing and ending blank spaces in folder and file names on Windows in bulk

I tried following Remove leading spaces in Windows file names but it's not working for my use case.
I have a lot of folders and filenames that either have a blank space at the front or at the end. How would I go about removing those spaces in bulk?
This was the command-line command I used after following the linked post:
for /R %A IN ("* ") do #for /F "tokens=*" %B IN ("%~nxA") do #ren "%A" "%B"
But it didn't work out.
Update: thank you to all who replied trying to help. I think there is just a Windows-level glitch in the file system. I ended up just having to manually create new folders without leading and trailing spaces and then dragging all the files over manually then renaming those to non-trailing and leading names as well.
It's unclear whether or not you want a PowerShell solution, but there's a reasonable assumption to be made you might.
If you wanted a PowerShell solution, you could try this:
function Test-LeadingTrailingWhitespace {
param(
[Parameter(Mandatory)]
[String]$String
)
$String[0] -eq ' ' -Or $String[-1] -eq ' '
}
Get-ChildItem -Path "<path_to_folder>" | ForEach-Object {
if ($_.PSIsContainer -And (Test-LeadingTrailingWhitespace -String $_.Name)) {
$Destination = Split-Path -Path $_.FullName -Parent
$NewName = $_.Name.Trim()
Move-Item -Path $_ -Destination (Join-Path -Path $Destination -ChildPath $NewName)
}
elseif (Test-LeadingTrailingWhitespace -String $_.BaseName) {
$Destination = Split-Path -Path $_.FullName -Parent
$NewName = $_.BaseName.Trim() + $_.Extension
Move-Item -Path $_ -Destination (Join-Path -Path $Destination -ChildPath $NewName)
}
}
To be on the safe side, you could add -WhatIf or -Confirm on the Move-Item cmdlet. The former will tell you what would have changed without that parameter without actually making any changes (like a 'dry run'). The latter will prompt you for confirmation before making each change, giving you a chance to validate incrementally and not make changes en masse from the moment you hit enter.
Trim() is a method available for all strings in PowerShell:
Returns a new string in which all leading and trailing occurrences of a set of specified characters from the current string are removed.
You can loop over files and folder and check if they actually have a leading or trailing whitespace before renaming, this would avoid errors like:
Rename-Item: Source and destination path must be different.
We can use the -match matching operator with a simple regex ^\s|\s$ (starts with whitespace or ends with whitespace - regex101 link for a simple example) to see if the file or folder should be renamed:
Get-ChildItem path\to\startingfolder -Recurse | ForEach-Object {
$newName = switch($_) {
# handle folders
{ $_.PSIsContainer -and $_.Name -match '^\s|\s$' } {
$_.Name.Trim()
break
}
# handle files
{ $_.BaseName -match '^\s|\s$' -or $_.Extension -match '^\s|\s$' } {
$_.BaseName.Trim() + $_.Extension.Trim()
break
}
# if none of the above conditions were true, continue with next item
Default {
return
}
}
Rename-Item -LiteralPath $_.FullName -NewName $newName
}
Personally, I'd do this in two steps to rename folders and files separately. This to overcome the problem that when a folder is renamed, the items inside that folder all have a new path.
Using switch -Force allows renaming items such as hidden or read-only files
Using -ErrorAction SilentlyContinue swallows the error when the new name is equal to the existing name
$rootPath = 'X:\thepath'
# first the folders and subfolders (deepest nesting first)
(Get-ChildItem -Path $rootPath -Directory -Recurse | Sort-Object FullName -Descending) |
Rename-Item -NewName {$_.Name.Trim()} -Force -ErrorAction SilentlyContinue
# next the files
(Get-ChildItem -Path $rootPath -File -Recurse) |
Rename-Item -NewName {'{0}{1}' -f $_.BaseName.Trim(), $_.Extension.Trim()} -Force -ErrorAction SilentlyContinue

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\*\*\*

Folder deleting after script ends

I am currently writing a script that takes a folder of files, moves the first file to a folder with a specific name, then move the rest to another folder with a number for a name.
My script works however it also moves the folder and renames it too. Which section of the code is causing this?
$path = "C:\Users\User1\Desktop\MergeTest\_First\"
$FileCount = Get-ChildItem -Path $path -File | Measure-Object | %{$_.Count}
$FirstFile = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
$FinalReport = "C:\Users\User1\Desktop\MergeTest\___Final\TestOutput.xlsx"
Move-Item "C:\Users\User1\Desktop\MergeTest\_First\$FirstFile" $FinalReport
$Counter = 0;
Write-host $FileCount
for($Counter = 0; $Counter -lt $FileCount; $Counter++)
{
$FileInWork = Get-ChildItem -Path $path -Force -File | Select-Object -First 1
move-item "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork" "C:\Users\User1\Desktop\MergeTest\__Second\$Counter.xlsx"
Write-host "File Moved"
}
What you could do is specify the -Include *.txt condition to your move-item commands so it is only to move just .txt, .log, or whatever file type you're moving and leave the folder how it is.
I believe your code could do with some cleaning up. Now you are executing Get-ChildItem 3 times, where using it once is enough.
Also, you should try and use the Join-Path rather than constructing the path and filenames yourself.
Especially where you do "C:\Users\User1\Desktop\MergeTest\_First\$FileInWork", you should realize that Get-ChildItem returns FileInfo and/or DirectoryInfo objects; not strings.
Anyway, the below code should do what you want:
# define the path where all other paths are in
$rootPath = "C:\Users\User1\Desktop\MergeTest"
# create the working paths using the common root folder path
$filesPath = Join-Path -Path $rootPath -ChildPath '_First'
$firstDestination = Join-Path -Path $rootPath -ChildPath '___Final'
$secondDestination = Join-Path -Path $rootPath -ChildPath '__Second'
# test if the destination folders exist and if not create them
if (!(Test-Path -Path $firstDestination -PathType Container)) {
Write-Host "Creating folder '$firstDestination'"
$null = New-Item -Path $firstDestination -ItemType Directory
}
if (!(Test-Path -Path $secondDestination -PathType Container)) {
Write-Host "Creating folder '$secondDestination'"
$null = New-Item -Path $secondDestination -ItemType Directory
}
# get an array of all FileInfo objects in $filesPath
# you could consider adding -Filter '*.xlsx' here..
$allFiles = Get-ChildItem -Path $filesPath -Force -File
Write-Host 'Total number of files found: {0}' -f $allFiles.Count
# move the files
for ($i = 0; $i -lt $allFiles.Count; $i++) {
if ($i -eq 0) {
# the first file should go in the $firstDestination folder with specified name
$target = Join-Path -Path $firstDestination -ChildPath 'TestOutput.xlsx'
}
else {
# all other files go to the $secondDestination folder
# each file should have the index number as name
$target = Join-Path -Path $secondDestination -ChildPath ('{0}.xlsx' -f ($i + 1))
}
$allFiles[$i] | Move-Item -Destination $target -Force -WhatIf
}
Hope that helps
Remove the -WhatIf if you are satisfied with whatever the output on console shows.
P.S. I really think you should edit your question and change its title, because nothing in the question has to do with Folder deleting after script ends..

Powershell script halting and not working as expected

I have written a script to get system variables and copy of several folders ,I wanted to create a directory for copy of several folders,to prevent duplication of folders we wanted a check condition so each time we run the script it is not creating folders. Like an example
$nfle=New-Item -ItemType "Directory" -Path "D:\Temp\" -Name "foo"
[bool]$checkfle=Test-Path "D:\Temp\foo" -PathType Any
if ( $checkfle -eq $True)
{
Write-Output "$nfle Exists"
}
else
{
$bnfle=New-Item -ItemType "Directory" -Path "D:\Temp\" -Name ("boo")
}
$cpypste=Copy-Item "D:\Temp\foo" -destination "D:\Temp\boo"
Write-Host "Succesful Copy of Folders"
So when we run the script it is creating folder foo,again when we run the script , it is displaying foo exists, and stopping the script is not going to next line, not even displaying the message.Is there a way in powershell to find out why the script is stopping or shall i add more information statements. TIA
It best to start with test-path to see if the folder is there. A "Container" is a folder/directory. Then check if you need to write the folder.
# This should allow your script to continue of error.
$ErrorActionPreference = "Continue"
# check if "C:\Temp\Foo" exist. if not make C:\Temp\foo"
$nfle = 'C:\Temp\foo'
[bool]$checkfle = Test-Path $nfle -PathType Container
if ( $checkfle -eq $True)
{
Write-Output "$nfle Exists"
}
else
{
New-Item -ItemType "Directory" -Path "C:\Temp\" -Name "foo"
}
# check if "C:\Temp\boo" exist. if not make C:\Temp\boo"
$BooFilePath = "C:\Temp\boo"
[bool]$checkboo = Test-Path $BooFilePath -PathType Container
if ( $checkboo -eq $True)
{
Write-Output " $BooFilePath Exists"
}
else
{
New-Item -ItemType "Directory" -Path "C:\Temp\" -Name "boo"
}
# This makes the folder C:\Temp\boo\foo.
# $cpypste = Copy-Item -Path "C:\Temp\foo\" -destination "C:\Temp\boo\"
# If you want copy the contents of foo into boo you will need * or -recurse
$cpypste = Copy-Item -Path "C:\Temp\foo\*" -destination "C:\Temp\boo\" -PassThru
Write-Host "Succesful Copy of Folders"
$cpypste.FullName
I have tried the demo provided and it works from my side, multiple times, so I was not able to re-create the problem.
If you would like to debug scripts in PowwerShell, you may follow this link:
https://learn.microsoft.com/en-us/powershell/scripting/components/ise/how-to-debug-scripts-in-windows-powershell-ise?view=powershell-6
I am not sure, why you are storing the result of Copy-Item into a variable, as it is null?
Hope it helps!

Resources