I want each row to be populated with the data retrieved from each file. Currently, the 2nd and 3rd column entries are being written to a newline.CSV file output I have tried using "export-csv" and the "-nonewline" command. Perhaps there is a regex command that would solve this?
#Column headings
$headings = "Source file, Review file existence, Review Result, Compilation Check Result, Static Analysis Result, Review up-to-date, Reviewed code version, Latest code version"
# Create array with $headings as first input
$output = #($headings)
$SourceParentDir = "C:\Document"
$Files = get-childitem -Path $SourceParentDir -Recurse -Filter '*.?pp' | % { $_.FullName }
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
# Populate each row for each file
$output += $BaseName
$output += ", Review Exists" # writes to a newline
$output += ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
}
# write output to a csv file
$output | Out-File -FilePath Documents\Example-csv.csv -encoding utf8
You can do things that way, but there's definitely a more-Powershelley way:
Get-ChildItem -Path $SourceParentDir -Recurse -Filter '*.?pp' |
ForEach-Object {
$File = $_
# Put your other code here
# This will output an object to the stream
[PSCustomObject]#{
'Source file' = $File.Name
'Review file existence' = 'Review Exists'
'Review Result' = $Result
'Compilation Check Result' = $Compilation
'Static Analysis Result' = $StaticAnalysis
'Review up-to-date' = $UpToDateFlag
'Reviewed code version' = $ReviewFileVersionNumber
'Latest code version' = $SourceFileVersionNumber
}
} | Export-Csv Example-csv.csv -NoTypeInformation
The big drawback here is that you don't get a lot of formatting choices about the CSV. Every field is quoted, for example.
Alternately, if you really want really detailed control of the $output string, you should use a StringBuilder instead of a String. StringBuilder is one of the most potent and widely used classes in C#. This is because strings in C# and Powershell are immutable, so when you += a String you create a new string, copy everything over with the new bit, then throw the old string away. It can be very memory intensive with large operations. StringBuilder lets you get around all that. It's a class that's designed to let you append stuff to strings and format them however you want.
You instance it like so:
$output = [System.Text.StringBuilder]::new()
And then you typically call one of two methods to add text. Append($string) appends the string, AppendLine($string) appends the line and then adds a newline. You can also call AppendLine() with no argument to just add a newline. To get your final string, you call the ToString() method. The append methods do return a status when you call them which you can prevent from outputting pretty easily with a [void], or by saving it to another variable if you need it.
$output = [System.Text.StringBuilder]::new()
[void]$output.AppendLine($headings)
$SourceParentDir = "C:\StarTeam\00011114-JSENS_TRS\ATR\04_SW_Implementation\Operational"
$Files = get-childitem -Path $SourceParentDir -Recurse -Filter '*.?pp' | % { $_.FullName }
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
# Populate each row for each file
[void]$output.Append($BaseName)
[void]$output.Append(", Review Exists")
[void]$output.Append(", $Result,$Compilation,$StaticAnalysis,$UpToDateFlag,$ReviewFileVersionNumber,$SourceFileVersionNumber,")
[void]$output.AppendLine()
}
$output.ToString() | Out-File -FilePath Documents\Example-csv.csv -encoding utf8
$output is an array, so each of those += inside the loop is a new entry in the array, and therefore a new line in the file.
You can fix this by using a temporary string variable in the middle of the loop, and appending it to $output once at the end of each iteration:
foreach ($File in $Files)
{
$row = [System.IO.Path]::GetFileName($File)
$row += ", Review Exists"
$row += ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
$output += $row
}
or by putting everything in one big string interpolation:
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
$output += "$BaseName, Review Exists, $Result, $Compilation, $StaticAnalysis, $UpToDateFlag, $ReviewFileVersionNumber, $SourceFileVersionNumber"
}
But I agree with the other answer that building up an array of custom objects for the Export-Csv commandlet is more idiomatic for PowerShell.
The issue is how you're populating $Output. Since it is defined as an array, each time you're adding new information it's creating a new entry rather than just adding the additional information to a string.
If you make your $output into one line with all required fields it should correct it without you changing the array.
$output += $BaseName + ", Review Exists" + ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
I want to create a script that re-runs a batch file if a specific keyword is found within a log. The problem that I am having is with the function used to check the file. Currently, when I run this script it exits saying that the expression "does not match" even though the keyword does, in fact, exist within the log file. In this case, the log file to check is named is 'output.log' and the keyword to match is called 'temporary'.
$current_date = Get-Date -Format yyyyMMdd
$file_path = "backup_" + $current_date
"checking in directory... --> " + $file_path
$word_to_find = "temporary"
$file_to_check = "output.log"
"Searching for matching expression '" + $word_to_find + "' in file: " + $file_to_check
$containsWord = $file_to_check | %{$_ -match $word_to_find}
if ($containsWord -contains $true) {
'The expression matches, re-running batch feed.'
start .\batch_script.bat
} else {
'The expression does not match. Feed OK.'
}
Thats because you first have to get the content of the log, atm you are just comparing the string "output.log" against the string "temporary", which ofc returns the value 'false'.
If you want to keep your attempt, try it like this (Remember that output.log should either be in the running directory (than use .\ like here) or you have to give the full path to the log file):
$current_date = Get-Date -Format yyyyMMdd
$file_path = "backup_"+ $current_date
"checking in directory... --> " + $file_path
$word_to_find="temporary"
$file_to_check=".\output.log"
"Searching for matching expression '" + $word_to_find + "' in file: " + $file_to_check
$containsWord = Get-Content $file_to_check | %{$_ -match $word_to_find}
If ($containsWord -contains $true) {
'The expression matches, re-running batch feed.'
start .\batch_script.bat
}
Else {
'The expression does not match. Feed OK.'
}
If you are up to some improvements, I would do it more like this:
$current_date = Get-Date -Format yyyyMMdd
$file_path = "backup_"+ $current_date
Write-Host "checking in directory... --> $file_path"
$word_to_find="temporary"
$file_to_check="<fullPathToLog>\output.log"
Write-Host "Searching for matching expression '" + $word_to_find + "' in file: $file_to_check"
If((Get-Content $file_to_check) -match $word_to_find) {
'The expression matches, re-running batch feed.'
start .\batch_script.bat
}
Else {
'The expression does not match. Feed OK.'
}
This would save you the foreach and the extra variable.
I have this Powershell code:
Function CheckFileList()
{
$limit = (Get-Date).AddDays(-270)
$input_path = gci '//network/sourceDir' | sort -property LastWriteTime
$output_file = 'c:\PowershellScripts\prune_results.txt'
#Clear-Content $output_file
$countf = 0
$outputstr = ""
$outputstr = $(Get-Date -format 'F') + " - Folders to be purged:`r`n"
$input_path | Foreach-Object{
if ( (Get-Item $_.FullName) -is [System.IO.DirectoryInfo] ) {
if ( $_.LastWriteTime -le $limit ) {
$source='//network/sourceDir' + $_.Name
$dest="\\computer\c$\targetDir" + $_.Name
$what=#("/MOVE")
$options=#("/COPY:DAT /DCOPY:T")
$cmdArgs = #("$source","$dest",$what,$options)
#"robocopy " + $cmdArgs >> $output_file
robocopy #cmdArgs
$outputstr = $outputstr + " (" + $_.LastWriteTime + ") `t" + $_.Name + "`r`n"
$countf++
$outputstr = $outputstr + "Folders [to be] purged: " + $countf + "`r`n`r`n"
$outputstr >> $output_file
Exit
}
}
}
$outputstr = $outputstr + "Folders [to be] purged: " + $countf + "`r`n`r`n"
$outputstr >> $output_file
}
CheckFilelist
This is intended to move many folders (and the files in them) while preserving the folder timestamps.
When I run it, I get this error:
-------------------------------------------------------------------------------
ROBOCOPY :: Robust File Copy for Windows
-------------------------------------------------------------------------------
Started : Mon Apr 27 13:20:35 2015
Source - \\network\sourceDir\someFolder12345\
Dest - \\computer\c$\someFolder12345\
Files :
Options : /COPY:DAT /MOVE /R:1000000 /W:30
------------------------------------------------------------------------------
ERROR : Invalid Parameter #4 : "/COPY:DAT /DCOPY:T"
Simple Usage :: ROBOCOPY source destination /MIR
source :: Source Directory (drive:\path or \\server\share\path).
destination :: Destination Dir (drive:\path or \\server\share\path).
/MIR :: Mirror a complete directory tree.
For more usage information run ROBOCOPY /?
**** /MIR can DELETE files as well as copy them !
Is there something wrong with my what/options array? The parameters look valid to me.
[EDIT] I'm also finding this script is not preserving folder timestamps. someFolder12345 ends up on the targetDir with the date/time of "now". The files within the folder are preserving timestamps, but not the folder?
It looks like your string "/COPY:DAT /DCOPY:T" is being passed to robocopy as one argument, not as 2 separate arguments. If you check the $options variable, it has a single item in the array. Try changing that line to $options=#("/COPY:DAT","/DCOPY:T") so that each argument is passed in separately.
I have created a simple GUI to enter some values that are stored in a .txt file as soon as the User clicks OK.
For verification I'm displaying the data of the just created file with its input in a popup window.
As I want to use the data of the config file in several other .ps1 files that I'm using for a project, I started to move things into a globals.ps1 file. Everything works great, with the exception that I can no longer display the hashtable.
Here's what I have in my globals.ps1 :
# Wshell popup
$wshell = New-Object -ComObject Wscript.SHell
# read config
function readcfg
{
Get-Content -Path $cfg | foreach-object -begin { $conf = #{ } } -process {`
$key = [regex]::split($_, ':');
if (($key[0].CompareTo("") -ne 0) -and ($key[0].StartsWith("[") -ne $True))`
{ $conf.Add($key[0], $key[1]) }
}
}
And this is the part in my settings GUI which is executed if the OK button is pressed:
$cfgData = "
[Account]
Screds:" + $Admaccount.text + "
Spw:" + $PW + "
[Domainconfig]
Sdomain:" + $domain.text + "
SSearchBase:" + $searchBase.text + "
SdeactivatedUsers_OU:" + $deactivatedUsersOU.text + ""
Out-File -filepath $cfg -inputobject $cfgData -Force
Start-Sleep -s 1
$wshell.Popup("Settings saved:`nAccount: " + $conf.Screds + "`nDomain: " + $conf.Sdomain + "`nSearchBase: " + $conf.SSearchBase + "`ndeactivatedUsersOU " + $conf.SdeactivatedUsers_OU + "", 0, "Yarr...!", 0x0)
If I move the get-Content out of the readcfg function, I can display the values again. But that's of course not the solution, as it will display the old data if the settings are changed and the popup comes up again.
What am I missing here?
As stated by Kayasax in the comments, it was simply a "out of scope" issue.
after declaring the function and hashtable as global, it all works perfectly again.
# read config
function global:readcfg
{
Get-Content -Path $cfg | foreach-object -begin { $global:conf = #{ } } -process {`
$key = [regex]::split($_, ':');
if (($key[0].CompareTo("") -ne 0) -and ($key[0].StartsWith("[") -ne $True))`
{ $global:conf.Add($key[0], $key[1]) }
}
}
I writing a script in Powershell to gather information about how many files are in my archive folder, the script is to count the files in the folder then it deletes file older than give date and then it counts the number of files again to report how many files it deleted.
I can get this to work however I am running into a little problem with the syntax. When I have it like this it works fine.
Clear-Host
$ErrorFiles = "c:\data\ErrorFiles"
$Archivefiles = "c:\data\Archivefiles"
$PreCountErrorFiles = ((Get-ChildItem $ErrorFiles | Measure-Object).Count)
$PreCountArchivefiles = ((Get-ChildItem $Archivefiles | Measure-Object ).Count)
Get-ChildItem $ErrorFiles | Where {$_.LastWriteTime –lt ((Get-Date).AddDays(-10) )} | ForEach { Remove-Item $_.FullName }
Get-ChildItem $Archivefiles | Where {$_.LastWriteTime –lt ((Get-Date).AddDays(-10) )} | ForEach { Remove-Item $_.FullName }
$PostCountErrorFiles = ((Get-ChildItem $ErrorFiles | Measure-Object).Count)
$PostCountArchivefiles = ((Get-ChildItem $Archivefiles | Measure-Object ).Count)
$str1 = "Number of files in " + $ErrorFiles.ToString() + " was " + $PreCountErrorFiles.ToString() + " number of files deleted " + ($PreCountErrorFiles - $PostCountErrorFiles).ToString() + " and " + $PostCountErrorFiles.ToString() + " are left." + "`n"
$str2 = "Number of files in " + $Archivefiles.ToString() + " was " + $PreCountArchivefiles.ToString() + " number of files deleted " + ($PreCountArchivefiles - $PostCountArchivefiles).ToString() + " and " + $PostCountArchivefiles.ToString() + " are left." + "`n"
write-host $str1
write-host $str2
However if I try to change the #str1 assignment to have it in one line and take both the strings I get this error (see code below)
Cannot convert value "Number of files in" to type "System.Int32". Error: "Input string was not in a correct format."
At line:13 char:1
+ $str1 = "Number of files in " + $ErrorFiles.ToString() + " was " + $PreCountErro ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastFromStringToInteger
Clear-Host
$ErrorFiles = "c:\data\ErrorFiles"
$Archivefiles = "c:\data\Archivefiles"
$PreCountErrorFiles = ((Get-ChildItem $ErrorFiles | Measure-Object).Count)
$PreCountArchivefiles = ((Get-ChildItem $Archivefiles | Measure-Object ).Count)
Get-ChildItem $ErrorFiles | Where {$_.LastWriteTime –lt ((Get-Date).AddDays(-10) )} | ForEach { Remove-Item $_.FullName }
Get-ChildItem $Archivefiles | Where {$_.LastWriteTime –lt ((Get-Date).AddDays(-10) )} | ForEach { Remove-Item $_.FullName }
$PostCountErrorFiles = ((Get-ChildItem $ErrorFiles | Measure-Object).Count)
$PostCountArchivefiles = ((Get-ChildItem $Archivefiles | Measure-Object ).Count)
$str1 = "Number of files in " + $ErrorFiles.ToString() + " was " + $PreCountErrorFiles.ToString() + " number of files deleted " + ($PreCountErrorFiles - $PostCountErrorFiles).ToString() + " and " + $PostCountErrorFiles.ToString() + " are left." + "`n" +
- "Number of files in " + $Archivefiles.ToString() + " was " + $PreCountArchivefiles.ToString() + " number of files deleted " + ($PreCountArchivefiles - $PostCountArchivefiles).ToString() + " and " + $PostCountArchivefiles.ToString() + " are left." + "`n"
write-host $str1
Thanks for you help!
Remove the hyphen from the beginning of the second line:
$str1 = "Number of files in " + $ErrorFiles.ToString() + " was " + $PreCountErrorFiles.ToString() + " number of files deleted " + ($PreCountErrorFiles - $PostCountErrorFiles).ToString() + " and " + $PostCountErrorFiles.ToString() + " are left." + "`n" +
- "Number of files in " + $Archivefiles.ToString() + " was " + $PreCountArchivefiles.ToString() + " number of files deleted " + ($PreCountArchivefiles - $PostCountArchivefiles).ToString() + " and " + $PostCountArchivefiles.ToString() + " are left." + "`n"
^
In genereal, the format operator (-f) is a better approach to building strings like this:
$str1 = "Number of files in {0} was {1} number of files deleted {2} and {3} " +
"are left.`nNumber of files in {4} was {5} number of files deleted " +
"{6} and {7} are left.`n"
$str1 -f $ErrorFiles, $PreCountErrorFiles,
($PreCountErrorFiles - $PostCountErrorFiles),
$PostCountErrorFiles, $Archivefiles, $PreCountArchivefiles,
($PreCountArchivefiles - $PostCountArchivefiles),
$PostCountArchivefiles