5

I want to output each line in a text document into its own document where the output documents are named 1.txt, 2.txt, etc. This is my initial code that reads the text documents and outputs the lines one by one.

get-content "artists.txt" | Foreach-Object {$_ | set-content "artists\$counter.txt"}

But as you can imagine, this outputs always to the same document.

I have tried

get-content "artists.txt" | 
  Foreach-Object {$_ | set-content "artists\$counter.txt" } {$counter++}

as I have seen suggested online and while this increases the counter, it doesn't do what I want (since it still outputs to the same file).

Not really sure where to insert the counter++ here as inside the Foreach-Object loop itself gives me an error (unless I'm inserting it wrong).

Thanks.

3 Answers 3

5

In your case, you can use the ReadCount property returned by the Get-Content cmdlet:

get-content "artists.txt" | Foreach-Object {$_ | set-content "artists\$($_.ReadCount).txt" }
Sign up to request clarification or add additional context in comments.

Comments

3

Martin Brandl's helpful answer provides an effective solution based on the - obscurely named - ReadCount property that Get-Content adds to each input line, which reflects the 1-based line number.

Using a delay-bind script block enables a solution that is both shorter and noticeably faster:

Get-Content artists.txt | Set-Content -LiteralPath { "artists\$($_.ReadCount).txt" }

As for what you tried:

Variable $counter must be incremented in the ForEach-Object command's -Process block, i.e., the block that is executed for each input object (and is typically the only block specified, positionally).

The 2nd script block you passed, {$counter++}, binds to the -End parameter, meaning the block to be executed once, after all pipeline objects have been received.

Therefore, you could have used the following:

$counter = 0
Get-Content artists.txt | Foreach-Object { 
  $_ | Set-Content "artists\$((++$counter)).txt"
}

The increment operation is embedded in the expandable string for brevity, but you could make it a separate statement.

Note the use of an extra pair of (...) around expression ++$counter, so as to ensure that the expression's value is also output; by default, ++ just increments a variable's value, but doesn't produce output.
The outer $(...) - the subexpression operator - is needed in order to embed expressions or commands in expandable strings ("...").

Comments

2

In one-liners, the For-Each object has three positional blocks: begin, process and end:

...<piped objects>... | For-Each {...begin...} {...process...} {...end...}

An easy and general way to set up a counter is:

...<piped objects>... | For-Each {$counter = 0} {...process...} {...end...}

because if you need to reuse the one-liner, the $counter variable will not be reset. You can also add code to the begin block to start the counter at something else as well.

For your particular case, we just have to initialize $counter in a begin block and use mklement0's answer:

get-content "artists.txt" | Foreach-Object {$counter = 0} {$_ | set-content "artists\$((++$counter.txt))"}

You could even use the end block to output a summary:

For-Each {...} {...} {Write-Host "There were $counter lines read and $counter files created"}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.