69

I have a simple textfile and I need a powershell script to replace some parts of the file content.

My current script is the following:

$content = Get-Content -path "Input.json"

$content -Replace '"(\d+),(\d{1,})"', '$1.$2' |  Out-File "output.json"

Is it possible to write it in one line without the content variable, like this?

Get-Content -path "Input.json" | ??? -Replace '"(\d+),(\d{1,})"', '$1.$2' |  Out-File "output.json"

I don't know how I can use the output of the first get-content commandlet in the second command without the $content variable? Is there an automatic powershell variable

Is it possible to do more replacements than one in a pipeline.

Get-Content -path "Input.json" | ??? -Replace '"(\d+),(\d{1,})"', '$1.$2' | ??? -Replace 'second regex', 'second replacement' |  Out-File "output.json"
Martin Ender
  • 43,427
  • 11
  • 90
  • 130
Jan Baer
  • 1,105
  • 1
  • 12
  • 15

2 Answers2

106

Yes, you can do that in one line and don't even need a pipeline, as -replace works on arrays like you would expect it to do (and you can chain the operator):

(Get-Content Input.json) `
    -replace '"(\d+),(\d{1,})"', '$1.$2' `
    -replace 'second regex', 'second replacement' |
  Out-File output.json

(Line breaks added for readability.)

The parentheses around the Get-Content call are necessary to prevent the -replace operator being interpreted as an argument to Get-Content.

Joey
  • 344,408
  • 85
  • 689
  • 683
  • Although it only works on one-line-at-a-time `"\`n", "b" -replace('\n', 'a')` will return 'a', 'b' . `"\`n", "b" -replace('\nb', 'a')` Will still return "\`n", "b" – Nate Anderson Jun 02 '15 at 15:39
  • 3
    @TheRedPea: You can do it with all lines at once by using `Get-Content -Raw` instead. You then get a single string back. – Joey Oct 22 '15 at 07:32
  • Thanks Joey, that produces a single string back. My original comment is user-error -- as you said, your code should work on each element of the array. I think in my comment I meant to do this: `"`n", "b" -replace('[\nb]', 'a')` notice `'[\nb]'` instead of `'\nb'` . The result with square parentheses produces `"a", "a"` as expected. – Nate Anderson Oct 22 '15 at 22:12
  • 1
    @TheRedPea: Well, you were still correct in that the approach outlined in the answer is unsuitable for certain replacements, namely those that require the regex to look at subsequent lines, because each line in a single string and runs through the `-replace` individually. – Joey Oct 23 '15 at 07:48
  • You got it! That's what I must've been thinking. That "`n", "b" is equal to "\nb", and thus I expected "a" (not "a", "a"). Smart guy. – Nate Anderson Oct 23 '15 at 17:03
  • 3
    This messes with the encoding of the output file and puts a byte-order mark on the file. I did it like this to get it to output UTF-8 without bom: `$tmp = (Get-Content Input.json) -replace '"(\d+),(\d{1,})"', '$1.$2'; [System.IO.File]::WriteAllLines( 'output.json', $tmp) ` (Perhaps you need full path on the output filename). – Rory Aug 24 '16 at 09:07
  • 2
    `Set-Content -Path 'output.json'` also works to output text without a byte order mark. – Erik Barke Jun 11 '19 at 08:11
  • What are the back-ticks for around ` -replace '"(\d+),(\d{1,})"', '$1.$2' ` ?? – Bastion Oct 26 '20 at 23:25
  • You can use (?ms) for multi line replacements. It's an option at the beginning of the search expression. https://www.apharmony.com/software-sagacity/2014/08/multi-line-regular-expression-replace-in-powershell/ – Simon_Weaver Feb 14 '21 at 06:36
  • Do I need brackets if I have a var of the content as in `$xmlcontent = Get-Content $xml`. How would I work if I have `-replace('..','$($1)1$($2')`: instead of `.` between the capture groups a `1`. Is it correct? Does not work. – Timo Jul 03 '21 at 16:04
15

Is it possible to write it in one line without the content variable, like this?

Yes: use ForEach-Object (or its alias %) and then $_ to reference the object on the pipeline:

Get-Content -path "Input.json" | % { $_ -Replace '"(\d+),(\d{1,})"', '$1.$2' } |  Out-File "output.json"

Is it possible to do more replacements than one in a pipeline.

Yes.

  1. As above: just adding more Foreach-Object segments.
  2. As -replace returns the result, they can be chained in a single expression:

    ($_ -replace $a,$b) -replace $c,$d
    

    I suspect the parentheses are not needed, but I think they make it easier to read: clearly more than a few chained operators (especially if the match/replacements are non-trivial) will not be clear.

Richard
  • 106,783
  • 21
  • 203
  • 265
  • 2
    Your solution works only with the parentheses around the Get-Content command let. (Get-Content -path $inputFile) | % { $_ -Replace '"(\d+),(\d{1,})"', '$1.$2' -Replace '"(\d+)"', '$1' -Replace '_', ''} | Out-File $outputFile – Jan Baer Apr 28 '13 at 06:29
  • 6
    @JanBaer Those parentheses are only needed if `$InputFile` is the same as `$OutputFile`. – Richard Apr 28 '13 at 07:19
  • 1
    Have you got any references on why the parentheses makes writing to the input file possible? This was exactly the use case I needed, but I find it non-obvious. – Chris F Carroll Oct 14 '17 at 12:04
  • 3
    @ChrisFCarroll Because it causes the whole file to be read in first. Otherwise it will be read and processed one line at a time; and will still be open for reading when you come to try and write the first line. – Richard Oct 14 '17 at 12:09
  • The solution works **not**, when the input file and output file are the **same**. Then the file content becomes deleted. When I have a different output file, it works fine. – Beauty Sep 08 '21 at 13:54