Powershell v2 prompting for value in foreach - windows

This code runs in PowerShell v1 but not in PowerShell v2. It now prompts for a value:
Get-ChildItem -Path $source -Filter "*.journal" |
Where-Object { $_.Name -match 'SoundPC\[\d{1,12}-\d{1,12}\].journal' } | Sort-Object -Property CreationTime | ForEach-Object
{
$sourcefile = $_.Name
}
Can someone help?

The opening brace must be on the same line as the foreach command, currently its on a new line:
Foreach-Object {...

As Shay Levy pointed out, the brace must be on the same line as the ForEach-Object command.
... | ForEach-Object {
$sourcefile = $_.Name
}
Alternatively, you can use the backtick character ` at the end of the line to cause a continuation, which will then allow you to put the opening brace on the next line. Being a C# developer, I prefer this so that my braces can line up.
... | ForEach-Object `
{
$sourcefile = $_.Name
}

Related

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.

dos2unix format conversion error while execution through TeamCity

While trying to transfer file from Windows to Unix Azure environment, I am getting error dos2unix format error
dos2unix -o /xyz/home/ABC_efg.txt failed to execute dos2unix format change.
I tried to run a PS script to fix it but does seem to work .
Get-ChildItem -File -Recurse *.txt | % { $x = get-content -raw -path $_.fullname; $x -replace "`r`n","`n" | set-content -NoNewline -path $_.fullname }
Instead of using -replace, I would prefer to read the content(s) as string array and join these strings with "`n".
Something like this:
$files = Get-ChildItem -File -Recurse -Filter '*.txt' | Select-Object -ExpandProperty FullName
$files | ForEach-Object {
(Get-Content -Path $_) -join "`n" | Set-Content -Path $_ -NoNewline -WhatIf
}
Remove the -WhatIf switch if you are satisfied with the outout shown in the console.
Well, part of the issue is that you are piping a string to Set-Content and then trying to use that string to determine where to save the file. Try changing the last part from:
$x -replace "`r`n","`n" | set-content -NoNewline -path $_.fullname
to this:
set-content -NoNewline -path $_.fullname -value ($x -replace "`r`n","`n")
If that doesn't update the formatting like you expect it to you may need to use the -Encoding parameter for Set-Content. I'm not real familiar with encoding though, so I am not sure about that.

Remove suffix from actual file extension

I am trying to use Rename-Item to remove trailing characters including the hyphen from a filename, ex. 123456.001.zip-4.22815.ren to 123456-001.zip.
Rename-Item -NewName ($_.Name.split('-')[0])
seems to be something I am missing after the split.
The split operation must be performed in a scriptblock ({}). A simple expression (()) won't work.
... | Rename-Item -NewName { $_.Name.Split('-')[0] }
Add -replace '^(\d+)\.', '$1-' if you want the period replaced with a hyphen.
... | Rename-Item -NewName { $_.Name.Split('-')[0] -replace '^(\d+)\.', '$1-' }
I got my script to work with these changes;
$src = "D:\temp"
Get-ChildItem -path $src -filter *.ren | ForEach-Object {
Rename-item -path $_.FullName -newname $_.Name.Split('-')[0] }

powershell exporting to text file

I'm working on a script that checks folders in specific directory. For example, I run the script for first time, it generates me a txt file containing folders in the directory.
I need the script to add any new directories that are found to the previously created txt file when the script is run again.
Does anyone have any suggestions how to make that happen?
Here is my code so far:
$LogFolders = Get-ChildItem -Directory mydirectory ;
If (-Not (Test-Path -path "txtfilelocated"))
{
Add-Content txtfilelocated -Value $LogFolders
break;
}else{
$File = Get-Content "txtfilelocatedt"
$File | ForEach-Object {
$_ -match $LogFolders
}
}
$File
something like this?
You can specify what directory to check adding path to get-childitem cmdlet in first line
$a = get-childitem | ? { $_.psiscontainer } | select -expand fullname #for V2.0 and above
$a = get-childitem -Directory | select -expand fullname #for V3.0 and above
if ( test-path .\list.txt )
{
compare-object (gc list.txt) ($a) -PassThru | Add-Content .\list.txt
}
else
{
$a | set-content .\list.txt
}

How to replace string in files and file and folder names recursively with PowerShell?

With PowerShell (although other suggestions are welcome), how does one recursively loop a directory/folder and
replace text A with B in all files,
rename all files so that A is replaced by B, and last
rename all folders also so that A is replaced by B?
With a few requirements refinements, I ended up with this script:
$match = "MyAssembly"
$replacement = Read-Host "Please enter a solution name"
$files = Get-ChildItem $(get-location) -filter *MyAssembly* -Recurse
$files |
Sort-Object -Descending -Property { $_.FullName } |
Rename-Item -newname { $_.name -replace $match, $replacement } -force
$files = Get-ChildItem $(get-location) -include *.cs, *.csproj, *.sln -Recurse
foreach($file in $files)
{
((Get-Content $file.fullname) -creplace $match, $replacement) | set-content $file.fullname
}
read-host -prompt "Done! Press any key to close."
I would go with something like this:
Get-ChildItem $directory -Recurse |
Sort-Object -Descending -Property { $_.FullName } |
ForEach-Object {
if (!$_.PsIsContainer) {
($_|Get-Content) -replace 'A', 'B' | Set-Content $_.FullName
}
$_
} |
Rename-Item { $_.name -replace 'A', 'B' }
The Sort-Object is there to ensure that first children (subdirs, files) are listed and then directories after them. (12345)
Untested, but should give you a starting point:
$a = 'A';
$b = 'B';
$all = ls -recurse;
$files = = $all | where{ !$_.PSIsContainer );
$files | %{
$c = ( $_ | get-itemcontent -replace $a,$b );
$c | out-file $_;
}
$all | rename-item -newname ( $_.Name -replace $a,$b );
Untested, may be I'm more lucky ;-)
$hello = 'hello'
$world = 'world'
$files = ls -recurse | ? {-not $_.PSIsContainer}
foearch ($file in $files) {
gc -path $file | % {$_ -replace $hello, $world} | Set-Content $file
ri -newname ($file.name -replace $hello, $world)
}
ls -recurse | ? {$_.PSIsContainer} | ri -newname ($_.name -replace $hello, $world)
To use the same recursion:
$hello = 'hello'
$world = 'world'
$everything = ls -recurse
foearch ($thing in $everything) {
if ($thing.PSIsContainer -ne $true) {
gc -path $thing | % {$_ -replace $hello, $world} | Set-Content $thing
}
ri -newname ($thing.name -replace $hello, $world)
}
I needed this for myself and below slightly better version of the script.
I added followings:
Support for verbose parameter so you can actually see what changes script has made.
Ability to specify folders so you can limit changes.
Adding bower.json, txt and md in to include extensions.
Search and replace content first, do rename later.
Do not replace content if search string is not found (this avoids unnecessary change in modified date).
[CmdletBinding(SupportsShouldProcess=$true)]
Param()
$match = "MyProject"
$replacement = Read-Host "Please enter project name"
$searchFolders = #("MyProject.JS", "MyProject.WebApi", ".")
$extensions = #("*.cs", "*.csproj", "*.sln", "bower.json", "*.txt", "*.md")
foreach($searchFolderRelative in $searchFolders)
{
$searchFolder = join-path (get-location) $searchFolderRelative
Write-Verbose "Folder: $searchFolder"
$recurse = $searchFolderRelative -ne "."
if (test-path $searchFolder)
{
$files = Get-ChildItem (join-path $searchFolder "*") -file -include $extensions -Recurse:$recurse |
Where-Object {Select-String -Path $_.FullName $match -SimpleMatch -Quiet}
foreach($file in $files)
{
Write-Verbose "Replaced $match in $file"
((Get-Content $file.fullname) -creplace $match, $replacement) | set-content $file.fullname
}
$files = Get-ChildItem $searchFolder -filter *$match* -Recurse:$recurse
$files |
Sort-Object -Descending -Property { $_.FullName } |
% {
Write-Verbose "Renamed $_"
$newName = $_.name -replace $match, $replacement
Rename-Item $_.FullName -newname $newName -force
}
}
else
{
Write-Warning "Path not found: $searchFolder"
}
}
Note that one change from the answer is that above recurses folder only in specified folders, not in root. If you don't want that then just set $recurse = true.

Resources