I've got the below Powershell script, and I'm struggling to finish it off.
Get-ChildItem -Path 'C:\temp\xml' -Include '*.xml' |
ForEach-Object
{
$FileName = $_.Fullname
$Pattern = "</Date>"
$FileOriginal = Get-Content $FileName
$date = Get-Date
$DateStr = $date.ToString("yyyyMMdd")
[String[]] $FileModified = #()
Foreach ($Line in $FileOriginal)
{
$FileModified += $Line
if ($Line -match $pattern)
{
$FileModified += "<CDate>$DateStr</CDate>"
}
}
$FileModified = $FileModified -replace "CUR","PIT"
$FileModified = $FileModified -replace "Current","Time"
Set-Content $fileName $FileModified
}
When I attempt to run it, I get the following messages:
cmdlet ForEach-Object at command pipeline position 2
Supply values for the following parameters:
Process[0]:
Can anyone see what I'm doing wrong?
Put the curly brace on the same line as the foreach-object:
echo hi | foreach-object {
$_
}
This would fail, even in a script. It's a cmdlet, not a statement like "if", so it needs its -Process parameter on the same line.
echo hi | foreach-object
{
$_
}
Related
I'm trying to add a line in a .sln file before the $pattern. The only problem is that when I try to add the $firstOccurrence condition in the if statement it doesn't add anything at all. It still triggers the Write-Debug.
The first occurrence part is now commented out but I can't seem to find out why it doesn't write anything when I set the first occurrence.
Original source to my solution can be found here:
How to declare a variable and its type is Boolean in PowerShell?
$firstOccurrence = $true;
$pattern = "Global"
(Get-Content $fileName) | Foreach-Object {
#if ($firstOccurrence) {
if ($_ -match $pattern) {
Write-Debug "test"
$firstOccurrence = $false
#Add Lines after the selected pattern
"aaaaaaaaaaaaaaaaaaaaaaa"
}
#}
# send the current line to output
$_
} | Set-Content $fileName
You could also do this using (very fast) switch -Regex like:
$fileName = 'D:\Test\blah.txt'
$firstOccurrence = $true
$pattern = "Global"
$insert = "aaaaaaaaaaaaaaaaaaaaaaa"
$newContent = switch -Regex -File $fileName {
$pattern {
if ($firstOccurrence) {
$insert
$firstOccurrence = $false
}
$_
}
default { $_ }
}
$newContent | Set-Content $fileName -Force
Or did you perhaps mean this:
$fileName = 'D:\Test\blah.txt'
$pattern = "Global"
$insert = "aaaaaaaaaaaaaaaaaaaaaaa"
((Get-Content -Path $fileName -Raw) -split $pattern, 2) -join "$insert $pattern" | Set-Content $fileName -Force
?
Here's a method for deleting a line that I thought would work
#earlier in the script
$inFile = Get-Content -Path ".\input.txt"
# ...Later... #
$inFile = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $inFile
The problem is that the -notmatch parameter doesn't seem to work. The Get-Content cmdlet just copies all the content from input.txt, including $line. I've also tried changing the code to clear $inFile completley and create a temporary holder, but no dice.
Clear-Variable -name "inFile"
$holder = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $holder
$inFile = Get-Content -path ".\input.txt"
Am I using -notmatch incorrectly? Here's the full text script for context.
Write-Host "Starting"
[bool] $keepRunning = 1
[bool] $everFound = 0
[bool] $searchComplete = 0
:main while($keepRunning)
{
$inFile = Get-Content -path ".\input.txt"
$completed = Get-Content -Path ".\output.txt"
$line = $inFile[0]
$holder
if($inFile.count -eq 1)
{
$line = $inFile
}
# create condition to check if $line matches any line in completed.txt
# if it does, skip this line and move on to the next line
:search while($everFound -eq 0 -and $searchComplete -eq 0)
{
#Write-Host "Outer loop"
foreach($url in $completed)
{
#Write-Host $line
#write-host $url
if ($line -eq $url)
{
Write-Host "`nThis file was already downloaded --Skipping to the next line"
$inFile = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $inFile
$inFile = Get-Content -path ".\input.txt"
$line = $inFile[0]
$everFound = 1
break
}
}
if ($everFound -eq 1)
{
break
}
$searchComplete = 1
Write-Host "Search Complete`n"
}
Write-Host "Before the download--------"
Write-Host $everFound
Write-Host $searchComplete
if ($everFound -eq 0 -and $searchComplete -eq 1)
{
#download the files
$downloadCommand = "youtube-dl.exe --verbose --cookies .\cookies.txt `"$line`""
get-date
invoke-Expression $downloadCommand
#delete the url
add-content -Path ".\output.txt" -Value $line
$inFile = Get-Content -path ".\input.txt" | where-object {$_ -notmatch $line}
set-content -path ".\input.txt" -Value $inFile
write-host "`n"
get-date
Write-Host "Sleeping for 45mins"
#start-sleep -s 2700
}
$everFound = 0
$searchComplete = 0
Write-Host "-------------After the download!!"
Write-Host $everFound
Write-Host $searchComplete
# check if the file is empty. If it is, set the keepRunning flag to false and exit the main while loop
if($Null -eq $inFile)
{
$keepRunning = 0
}
}
Write-Host "Done"
Read-Host "Press the Enter Key to Exit"
EDIT:
$inFile contains a list of youtube URLs on each line. $line is assigned the value of the first line of $inFile
$line = $inFile[0]
Here is a youtube URL: https://www.youtube.com/watch?v=sB5zlHMsM7k
I also added some statements to output the values of $line right before the file. Someone please point me to the right direction.
Am I using -notmatch incorrectly?
You're using it incorrectly, if $line contains a substring to search for literally (as-is, verbatim) in the input file's lines, and that substring happens to contain regex metacharacters, such as . and $.
To use -match / -notmatch for literal substring matching, you must escape the substring:
$_ -notmatch [regex]::Escape($line)
If you want to match lines only in full, you must anchor the regex:
$_ -notmatch ('^' + [regex]::Escape($line) + '$')
Note that PowerShell has no operator for literal substring matching.
However, the System.String ([string]) type has a .Contains() method for literal substring matching, but not that, unlike PowerShell's operators, it is case-sensitive by default (there are overloads for case-insensitive matching, but only in PowerShell (Core) 7+):
-not $_.Contains($line) # case-sensitive, literal substring matching
# PS 7+ only: case-INsensitive, literal substring matching
-not $_.Contains($line, 'CurrentCultureIgnoreCase')
For full-line matching:
-not ($_.Length -eq $line.Length -and $_.Contains($line))
or:
-not $_.Equals($line, 'CurrentCultureIgnoreCase')
The advantage of using .Contains() is that it performs better than -match, though the latter offers much more flexibility.
Hi i have powershell where i am using a foreach-object and would like to skip the first iteration always. And i am using continue statement as well. But the current behaviour of continue is like break. Please suggest if something i am doing wrong here.
Below is the sample code.
$xmlfile = 'D:\testdirecotry\sample.xml'
[xml]$xmlcontent = (Get-Content $xmlfile)
$folderprefix = 'plm_z'
$regex = '<!--__AMAZONSITE id="(.+?)" instance="(.+?)"__-->'
$i=0
(Get-Content $xmlfile) | select-string -Pattern $regex | ForEach-Object {
write-host "Test Iterartion"
if($i -eq 0)
{
write-host "entering if loop"
write-host $i
$i++
write-host $i
continue
}
else
{
write-host "entering else loop"
write-host $_
$pscustomobject=#(
# write-host $_
$id = $_.Matches.Groups[1].Value
$instance = $_.Matches.Groups[2].Value
write-host "Do Something"
)
}
}
An easier way to skip the first object would potentially be to use Select-Object in the pipeline:
Get-Content $xmlfile |
Select-string -Pattern $regex |
Select-Object -Skip 1 |
ForEach-Object {
...
I have this code snippet where I try tro replace some strings in all files of a directory. I thought I could nest the foreach in the ForEach-Object, but this does not seem to work.
The error I get is:
InvalidArgument: (:) [ForEach-Object], ParameterBindingException
$files = Get-ChildItem $testdir\reference *.* -recurse
$replacementMap = #{"Fruit::Apple" = "NewApple";"Fruit::Banana" = "NewBanana"}
foreach ($file in $files)
{
If (Get-Content $($file.FullName) | Select-String -Pattern "Fruit::")
{
$content = Get-Content $($file.FullName) | ForEach-Object
{
$line = $_
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
This code worked without the
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
part. Anyone has a clue what I'm doing wrong? Thanks in advance
You missed a curly brace closure and formatting issue on the foreach-object. You need to take care of foreach and foreach-object in a different way:
Replace your existing foreach part with this:
foreach ($file in $files)
{
If(Get-Content $($file.FullName) | Select-String -Pattern "Fruit::")
{
$content = Get-Content $($file.FullName) | %{
$line = $_
foreach ($entry in $replacementMap.GetEnumerator())
{
$line -replace $($entry.Name),$($entry.Value)
}
}
$content = $content -join "`r`n"
$content | Set-Content $($file.FullName)
}
}
Instead of processing the files line-wise, just do the replacement operation on the entire file content at once. If the content has changed, overwrite the file.
$replacementMap = #{
"Fruit::Apple" = "NewApple"
"Fruit::Banana" = "NewBanana"
}
Get-ChildItem $testdir\reference -File -Recurse | foreach {
$content = Get-Content $_
$dirty = $false
foreach ($key in $replacementMap.Keys) {
$content = $content -replace $key,$replacementMap.$key
$dirty = $true
}
if ($dirty) { $content | Set-Content $_ }
}
Working on a code which will replace a set of characters from a text files in a folder. IS there a way where it can do it for all the files in the folder. I am using a Windows 7 OS and Powershell Version 3.Attaching the code which I have. The issue is it creates a new file when I run the code (New_NOV_1995.txt) but it doesn't change any character in the new file as mentioned in the code. Help very much Appreciated.
$lookupTable = #{
'¿' = '|'
'Ù' = '|'
'À' = '|'
'Ú' = '|'
'³' = '|'
'Ä' = '-'
}
$original_file = 'C:\FilePath\NOV_1995.txt'
$destination_file = 'C:\FilePath\NOV_1995_NEW.txt'
Get-Content -Path $original_file | ForEach-Object {
$line = $_
$lookupTable.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line = $line -replace $_.Key, $_.Value
}
}
$line
} | Set-Content -Path $destination_file
In the following example, I'm assuming that H:\Replace_String is a directory. In your code above, you don't have a backslash so it would only select files in the root of H:.
$configFiles = Get-ChildItem -path H:\Replace_String\*.txt
foreach ($file in $configFiles)
{
(Get-Content $file) |
Foreach-Object { $_ -replace "Cat", "New_Cat" } |
Foreach-Object { $_ -replace "Dog", "New_Dog" } |
Set-Content $file
}
The (original) answer proposed by Tony Hinkle needs another loop. The reason for this is that Get-Content produces an array. Each line represents an element of the array.
$configFiles = Get-ChildItem -path 'H:\Replace_String\*.txt'
foreach ($file in $configFiles){
$output = #()
$content = Get-Content $file
foreach ($line in $content) {
$line = $content.Replace("Cat", "New_Cat")
$line = $content.Replace("Dog", "New_Dog")
$output += $line
}
$output | Set-Content -Path $file
}
Edit: I noticed that Tony Hinkle's answer was modified as I posted this. He's sending everything through a pipeline where I'm storing the array in a variable then looping through. The pipeline method is probably more memory efficient. The variable with second loop for each element of the array is more easily modified to do more than just the two replacments.