Most likely you've fallen victim to Windows PowerShell's inconsistent stringification of the System.IO.FileInfo
instances output by Get-ChildItem
- see this answer.
The workaround is to use explicit stringification via the .FullName
property, which explicitly returns an item's full path.
Applied to your command, alongside some optimizations:
Get-ChildItem -File -Recurse src | ForEach-Object {
$f = $_.FullName # !! Explicitly retrieve the full path
(Get-Content $f -Raw) -creplace 'Find', 'Replace' |
Set-Content -NoNewline "$f.tmp"
Move-Item "$f.tmp" $f -Force
}
Get-Content -Raw
reads the entire file into memory as a single string, which is more efficient.
-creplace
(which performs case-sensitive replacement, as sed
would by default) is directly applied to the resulting multiline string and replaces all occurrences.
-NoNewline
(PSv5+) ensures that Set-Content
doesn't add an additional trailing newline to the multiline string being saved (the same applies to Out-File
/ >
).
Note: Given that Get-Content -Raw
reads the entire file up front, you could even write the modified content back to the very same file, without requiring an intermediate temporary file and a subsequent Move-Item
call; that said, doing so bears a slight risk of data loss, if the process of writing back to the same file is interrupted.
- Also, while your
sed
call retains the original file with extension .bak
, your PowerShell command does not.