Loop in powershell - powershell

Hello Guys need some help, tips with script:
$path = ".\" # path do txt
$server = "server" # server.txt
$paczki = ".\paczki\"
$missingi = "$path\$server.txt"
$plik = get-content $missingi
foreach ($j in $plik) {
Write-Output "1"
$wynik = Get-ChildItem "$paczki" | ? {$_.name -match "$j"}
if ($wynik -eq $null) {
# Write-Host $i
}
else {
Write-Output "2"
Write-Host $wynik "znaleziono"
Copy-Item $paczki\$wynik -Destination \\$server\c$\temp\ -force
}
}
#### BAT GENERATOR #####
Write-Output "3"
# & .\bat_generator.ps1
$zapis = "$path\test.bat"
"pushd %~dp0" > $zapis
$nazwa = Get-ChildItem "\\$server\c$\temp\" | select name
foreach ($i in $nazwa) {
$text = $i.name + " /norestart /quiet"
$text >> $zapis
}
"ppd0" >> $zapis # dodaj ppd0
move-item -path .\test.bat -destination \\$server\c$\temp\ -Force # skopiuj .bat na server
At first I create file with name of server, for example server.txt in this server we have list of KBs. Scripts searching in folder paczki that KB exist if yes copying this in server and create .bat
I would like do add automatically searching all .txt files eg server.txt, & server1.txt and use it in loop, I thought about something like that:
$pliki_txt= Get-ChildItem $path -Filter "*.txt" | % {$_.BaseName}
and put it in loop but its not really working, I try to add loop in this place:
for ($i in pliki_txt)
$path = ".\" # path do txt
$server="server" # server.txt
$pliki_txt= Get-ChildItem $path -Filter "*.txt" | % {$_.BaseName}
(....)
What am I doing wrong? Is there any easier way? Script is only working when I put manually set $server like $server="serwer"

You can try this:
$path = ".\"
Get-ChildItem $path -Filter *.txt | %{
$content = Get-content $_.FullName
Foreach($server in $content){
write-host $server
}
}

If I got that right, the issue here is that you're not putting the lines in the right order.
From your original code I would change the following
$path = ".\" # path do txt
$server = "server" # server.txt
$paczki = ".\paczki\"
# $missingi = "$path\$server.txt"
$missingi = Get-ChildItem -Path $path -Filter server*.txt | Select -ExpandProperty Name
foreach ($m in $missingi) {
$plik = get-content $m
( ... )
}
That way you'll check every server*.txt file in that path and process it accordingly.
Or you could even turn it into a parameterized script like this
Param(
[Parameter(Mandatory = $true)]
[String]$path,
[Parameter(Mandatory = $true)]
[String]$pattern,
[Parameter(Mandatory = $true)]
[String]$packzi
)
$missingi = Get-ChildItem -Path $path -Filter *.txt | Select -ExpandProperty | Select-String "$pattern"
foreach ($m in $missingi) {
$plik = get-content $m
foreach ($j in $plik) {
Write-Output "1"
$wynik = Get-ChildItem "$paczki" | ? {$_.name -match "$j"}
if ($wynik -eq $null) {
# Write-Host $i
}
else {
Write-Output "2"
Write-Host $wynik "znaleziono"
Copy-Item $paczki\$wynik -Destination \\$server\c$\temp\ -force
}
}
#### BAT GENERATOR #####
Write-Output "3"
# & .\bat_generator.ps1
$zapis = "$path\test.bat"
"pushd %~dp0" > $zapis
$nazwa = Get-ChildItem "\\$server\c$\temp\" | select name
foreach ($i in $nazwa) {
$text = $i.name + " /norestart /quiet"
$text >> $zapis
}
"ppd0" >> $zapis # dodaj ppd0
move-item -path .\test.bat -destination \\$server\c$\temp\ -Force # skopiuj .bat na server
}
Then you would run it like this:
.\YourScript.ps1 -path ".\" -pattern "server" -packzi ".\packzi\"
That will give you more flexibility if you want to change the source path, the name pattern or the search patch.
I hope this helps.

Related

Concatenating Output from Folder

I have thousands of PDF documents that I am trying to comb through and pull out only certain data. I have successfully created a script that goes through each PDF, puts its content into a .txt, and then the final .txt is searched for the requested information. The only part I am stuck on is trying to combine all the data from each PDF into this .txt file. Currenly, each successive PDF simply overwrites the previous data and the search is only performed on the final PDF in the folder. How can I alter this set of code to allow each bit of information to be concatenated into the .txt instead of overwriting?
$all = Get-Childitem -Path $file1 -Recurse -Filter *.pdf
foreach ($f in $all){
$outfile = -join ', '
$text = convert-PDFtoText $outfile
}
Here is my entire script for reference:
Start-Process powershell.exe -Verb RunAs {
function convert-PDFtoText {
param(
[Parameter(Mandatory=$true)][string]$file
)
Add-Type -Path "C:\ps\itextsharp.dll"
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
for ($page = 1; $page -le $pdf.NumberOfPages; $page++){
$text=[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,$page)
Write-Output $text
}
$pdf.Close()
}
$content = Read-Host "What are we looking for?: "
$file1 = Read-Host "Path to search: "
$all = Get-Childitem -Path $file1 -Recurse -Filter *.pdf
foreach ($f in $all){
$outfile = $f -join ', '
$text = convert-PDFtoText $outfile
}
$text | Out-File "C:\ps\bulk.txt"
Select-String -Path C:\ps\bulk.txt -Pattern $content | Out-File "C:\ps\select.txt"
Start-Sleep -Seconds 60
}
Any help would be greatly appreciated!
To capture all output across all convert-PDFtoText in a single output file, use a single pipeline with the ForEach-Object cmdlet:
Get-ChildItem -Path $file1 -Recurse -Filter *.pdf |
ForEach-Object { convert-PDFtoText $_.FullName } |
Out-File "C:\ps\bulk.txt"
A tweak to your convert-PDFtoText function would allow for a more concise and efficient solution:
Make convert-PDFtoText accept Get-ChildItem input directly from the pipeline:
function convert-PDFtoText {
param(
[Alias('FullName')
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[string] $file
)
begin {
Add-Type -Path "C:\ps\itextsharp.dll"
}
process {
$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
for ($page = 1; $page -le $pdf.NumberOfPages; $page++) {
[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,$page)
}
$pdf.Close()
}
}
This then allows you to simplify the command at the top to:
Get-ChildItem -Path $file1 -Recurse -Filter *.pdf |
convert-PDFtoText |
Out-File "C:\ps\bulk.txt"

How I can do this powershell script better?

When I move FenrirFS profile to another, paths in directories become wrong.
So I decided to make a ps script to resolve it.
$wdir = "files" # constant part of path
$path = $PSScriptRoot # path to script
$pfix = "Target=" # prefix of path
$files = Get-ChildItem -Path $path -Filter *.alias | Where { ! $_.PSIsContainer } | Select -Expand Name
foreach ($file in $files)
{
$filec = Get-Content $file
$nlin = 0 # counter of line
foreach ($line in $filec)
{
if($line.Contains($pfix))
{
$nline = $pfix + $path + '\' + $wdir + ($line -split $wdir)[1]
$filec[$nlin] = $filec[$nlin].replace($line,$nline)
$filec | Set-Content $file
break
}
$nlin++
}
}
It's work, but I have a lot of files, which I should replace.
And $filec | Set-Content $file a little bit dumby, cuz I need to replace only one line.
Example of file:
Target=E:\home\prj\polygon\ps\files\NoDigital\godTech_2.JPG
DisplayName=godTech_2.JPG
WorkDir=
Arguments=
ShowCmd=0
ps script is located in the directory with aliases.
p.s. powershell 5.1
You could use the much faster switch for that:
$wdir = "files" # constant part of path
$path = $PSScriptRoot # path to script
$pfix = "Target=" # prefix of path
$files = Get-ChildItem -Path $path -Filter '*.alias' -File | ForEach-Object {
$content = switch -Regex -File $_.FullName {
"^$pfix" {
$oldPath = ($_ -split '=', 2)[-1].Trim()
$childPath = Join-Path -Path $wdir -ChildPath ($oldPath -split $wdir, 2)[-1]
# output the new path
"$pfix{0}" -f (Join-Path -Path $path -ChildPath $childPath)
}
default { $_ }
}
$content | Set-Content -Path $_.FullName -Force
}

create symboliclink with powershell

I write script below to compare and create symboliclink with powershell.
$Source = "C:\Transcode\Powershell\abc&1"
$Destination = "C:\Transcode\Powershell\abc&2"
$filter = '*.txt'
$tmp = "$Source\tmp.log"
$log = "$Source\delete.log"
function New-SymLink ($link, $target)
{
if ($PSVersionTable.PSVersion.Major -ge 5)
{
New-Item -Path $link -ItemType SymbolicLink -Value $target
}
else
{
$command = "cmd /c mklink "
invoke-expression "$command ""$link"" ""$target"""
}
}
Get-ChildItem -Path $Source -Filter $filter -Recurse |
ForEach-Object {
$name=$_.BaseName
$ext=$_.Extension
$fileS=$_.FullName
$fileD="$Destination\$name$ext"
IF (Get-Content $tmp | Where-Object{$_ -match "$name"}){
New-SymLink ("""""$fileD""""","""""$fileS""""")
}
}
I get trouble with file name what contains symbol &.
I can create link by call command promt like this
cmd /c mklink """$Destination\$name$ext""" """$fileS"""
But I want to use function.
Can you help me resovle it
Try this (only partly tested) streamlined version which
escapes the & in $command with a caret ^&
removes redundancy and possibly unneeded quoting
## Q:\Test\2019\04\23\SO_55803869.ps1
$Source = "C:\Transcode\Powershell\abc&1"
$Destination = "C:\Transcode\Powershell\abc&2"
$filter = '*.txt'
$tmp = Get-Content "$Source\tmp.log"
$log = "$Source\delete.log"
function New-SymLink ($link, $target){
if ($PSVersionTable.PSVersion.Major -ge 5){
New-Item -Path $link -ItemType SymbolicLink -Value $target
} else {
$command = 'cmd.exe /c mklink "{0}" "{1}"' -f $link,$target
invoke-expression $command.Replace('&','^&')
}
}
Get-ChildItem -Path $Source -Filter $filter -Recurse | ForEach-Object {
$fileD= Join-Path $Destination $_.Name
IF ($tmp | Where-Object {$_ -match $_.BaseName} ) {
New-SymLink $fileD $_.FullName
}
}

Powershell Reading inner Folder

I put together a Robocopy script to backup data, but the problem I am having is the script is reading the parent folder and trying to execute the robocopy filters and error out.
what I would like for it to do is indent one and start copying the subfolder and use the filter there. the file structure is
Users\tim jones, sam adams
I just want the tim jones folder and the sam adams user folders with the Robocopy filter added to it. any help is welcomed
$Comp = Read-Host 'Please enter a computer name or IP'
do{
If (Test-Connection $Comp -quiet -Count 1) {
Write-host 'The host responded' -ForegroundColor "yellow"
Read-Host 'Press any key to continue...' | Out-Null
$ping = "ture"
Clear-Host
Get-WmiObject -ComputerName $Comp -class Win32_NetworkLoginProfile | Select- Object Name,#{label='LastLogon';expression={$_.ConvertToDateTime($_.LastLogon)}}
New-Item -Path \\skyzone\t$\sky\ -ItemType directory
$SourcePath = "\\$Comp\C$\Users\";
$TargetPath = "\\skyzone\t$\sky";
#Users libraries
$PicturesLibrary = ("\Pictures");
$Downloads = ("\Downloads");
$Favorites = ("\Favorites");
$Documents = ("\Documents");
$Desktop = ("\Desktop");
$Video = ("\Videos");
#User Outlook files and logs
$Outlook = ("\AppData\Local\Microsoft\Outlook")
$argsFromFolders = ("$SourcePath\$PicturesLibrary","$SourcePath\$Downloads","$SourcePath\$Favorites","$SourcePath\$Documents","$SourcePath\$Desktop","$SourcePath\$Video" ,"$SourcePath\$Outlook");
$argsToFolders = ("$TargetPath\Pictures","$TargetPath\Downloads","$TargetPath\Favorites","$TargetPath\Documents","$TargetPath\Desktop","$TargetPath\Videos","$TargetPath\Outlook");
For ($i=0; $i -lt $argsFromFolders.Length; $i++) {
Write-Host -ForegroundColor Green "Processing" $argsFromFolders[$i] "To" $argsToFolders[$i];
robocopy $argsFromFolders[$i] $argsToFolders[$i] *.* /MT:16 /XJ *.pst /R:3 /W:1 /NP /e /xf *.vmdk *.vmem *.iso *.exe *.ost desktop.ini /tee /Log+:
}
Your question is unclear and the sample is incomplete. You refer to $user which is never created and there are no robocopy-calls here. Because of this I can't give you a complete solution.
Take a look at this example source-path in your code:
$SourcePath = "\\localhost\C$\Users\";
$PicturesLibrary = ("\Pictures");
"$SourcePath\$PicturesLibrary"
#Output
\\localhost\C$\Users\\\Pictures
There are too many backslashes in the spot where you joined the two strings. Remove them from the variables or use Join-Path
$SourcePath = "\\localhost\C$\Users\"
$PicturesLibrary = "\Pictures"
Join-Path $SourcePath $PicturesLibrary
#Output
\\localhost\C$\Users\Pictures
Or
$SourcePath = "\\localhost\C$\Users"
$PicturesLibrary = "Pictures"
"$SourcePath\$PicturesLibrary"
#Output
\\localhost\C$\Users\Pictures
The username is missing. You need to use Get-Childitem on the $sourcePath to get the list of users in the first place and loop through those.
Sample:
$SourcePath = "\\localhost\C$\Users\"
Get-ChildItem -Path $SourcePath |
#Get folders only... And exlude public?
Where-Object { $_.PSIsContainer -and $_.Name -ne 'Public' } |
ForEach-Object {
#Code to run for each user-folder
$PicturesLibrary = ("Pictures")
Join-Path $_.FullName $PicturesLibrary
}
\\localhost\C$\Users\Default.migrated\Pictures
\\localhost\C$\Users\defaultuser0\Pictures
\\localhost\C$\Users\frode\Pictures
Update: Try something like this (untested).
$Comp = Read-Host 'Please enter a computer name or IP'
If (Test-Connection $Comp -quiet -Count 1) {
Write-host 'The host responded' -ForegroundColor "yellow"
Read-Host 'Press any key to continue...' | Out-Null
Clear-Host
Get-WmiObject -ComputerName $Comp -class Win32_NetworkLoginProfile | Select- Object Name,#{label='LastLogon';expression={$_.ConvertToDateTime($_.LastLogon)}}
$TargetPath = '\\skyzone\t$\sky'
if(-not (Test-Path -Path $TargetPath -PathType Container)) { New-Item -Path $TargetPath -ItemType Directory | Out-Null }
$Folderlist = ("Pictures","Downloads","Favorites","Documents","Desktop","Videos","Outlook");
Get-ChildItem -Path "\\$Comp\C$\Users\" |
#Get folders only... And exclude public?
Where-Object { $_.PSIsContainer -and $_.Name -ne 'Public' } |
ForEach-Object {
$user = $_.Name
foreach ($folder in $Folderlist) {
$from = Join-Path $_.FullName $folder
$to = Join-Path $TargetPath "$user\$folder"
Write-Host -ForegroundColor Green "Processing '$from' To '$to'"
robocopy $from $to *.* /MT:16 /XJ *.pst /R:3 /W:1 /NP /e /xf *.vmdk *.vmem *.iso *.exe *.ost desktop.ini /tee /Log+:\\apgrwkate1090f\t$\dren\$user\backup.txt
}
}
}

Piped dir within Foreach

I'm writing a script to delete pdf files older than 6 months in folder with the 'Email' prefix.
However, my second dir command within my foreach never runs, its code is blocked.
$Now = Get-Date;
$DaysTillDelete = "180";
$LastWrite = $Now.AddDays(-$DaysTillDelete);
$TargetFolder = "C:\Test EMDATA\EMDATA\";
$BackupPath = "\\SHPFS02\IT\EmPower Old";
$EmailFolders = #();
if(-Not(Test-Path -path ($TargetFolder + "\OldFiles" ))) {
mkdir -p ($TargetFolder +"\OldFiles");
}
$Network = Test-Path $BackupPath
#New-PSDrive -Name O -PSProvider FileSystem -Root "$BackupPath"; #-Credential $cred
Write-Host "Running Script"
dir $TargetFolder | %{
# Only delete files with the Email prefix
$name = $_.Name;
if ($_.Name.Length -le 5) {return;}
$id = $_.Name.SubString(0,5);
if ($id -eq "Email")
{
Write-Host "Found slip folder"
$EmailFolders += $TargetFolder + $_;
}
}
ForEach ($folder in $EmailFolders)
{
Write-Host $folder;
dir -path $folder -include *.pdf | %{
Write-Host "Checking" $name;
# Only select files older than 6 months
if( $_.LastWriteTime -le "$LastWrite")
{
$activeItem = Get-Item $TargetFolder + $_;
#Move files into oldfiles
Write-Host $TargetFolder
move-item -path $activeItem -destination ($TargetFolder + "OldFiles\");
if ($Network)
{
move-item -path $activeItem -destination "O:\";
}
Write-Host $_;
remove-item $activeItem;
Write-Host "Deleting" + $name;
}
}
}
The script works till line 31 but doesn't continue on past line 32 and being a fairly beginner PS user I can't see why.
Only use -include with the -recurse parameter.
http://technet.microsoft.com/en-us/library/hh849800.aspx
The Include parameter is effective only when the command includes the
Recurse parameter or the path leads to the contents of a directory,
such as C:\Windows*, where the wildcard character specifies the
contents of the C:\Windows directory.
What you want instead is the -filter parameter:
dir -path $folder -filter *.pdf