1

I'm trying to loop through the directory DirIn, remove the first two lines from each csv file, and then save those files in a new directory DirOut. The following code is what I've pieced together from other forums, but I cannot get the code to work. Any help would be greatly appreciated.

gci "DirIn\*.csv" | % {(gc $_ | select -Skip 2) | sc "DirOut\$_" }
pdwhoward
  • 17
  • 4
  • When making powershell csv files. I always add -notypeinformation to prevent these issues. This doesn't solve your problem. But may prevent it in the future. – Robert Cotterman Oct 13 '18 at 05:32

3 Answers3

1

You're losing your $_ reference to the file when piping inside the ForEach-Object loop. Use the -PipelineVariable parameter to get around that.

gci "DirIn\*.csv" -PipelineVariable 'File' | % {(gc $File | select -Skip 2) | sc "DirOut\$($File.Name)" }
TheMadTechnician
  • 34,906
  • 3
  • 42
  • 56
  • There is no need for `-PipelineVariable` in this case, because the `$_` is being referred to in the `sc` command inside an _argument_ in the OP's case, in which case `$_` still has the `gci`-produced value, so changing `$_` to `$($_.Name)` is enough. In short: @Nas' answer is sufficient. – mklement0 Oct 13 '18 at 02:22
1

To show LotPings answer in a readable and maintainable format...

ForEach ($File in (Get-ChildItem "DirIn\*.csv")) {
    (Get-Content $File | Select-Object -Skip 2) |
        Set-Content "DirOut\$($File.Name)"
}

Best practice is to not use aliases in script files.

lit
  • 14,456
  • 10
  • 65
  • 119
  • If you don't mind, I'd like to understand what's happening a bit better. Why is it DirOut\$($File.Name) instead of DirOut\$File.Name? – pdwhoward Oct 12 '18 at 20:02
  • 2
    If `DirOut\$File.Name` is not expanded, then `$File` will be interpreted alone, followed by the string ".Name". This would produce a name such as `DirOut\sample.csv.Name`. – lit Oct 12 '18 at 20:29
  • 1
    Sorry, the name without expansion might be something like `DirOut\C:\DirIn\sample.csv.Name`. – lit Oct 12 '18 at 20:36
  • 1
    All you're doing with this solution is to forgo the pipeline's object-by-object processing by collecting all `Get-ChildItem` output in memory up front, which isn't necessary. There is no need to switch to `foreach`, and @Nas' answer is the better choice (@LotPing's criticism didn't apply). – mklement0 Oct 13 '18 at 02:27
0
gci "DirIn\*.csv" | % {(gc $_ | select -Skip 2) | sc "DirOut\$($_.Name)" }
Nas
  • 1,243
  • 6
  • 7
  • The only difference to OPs line is `.Name` property of the no more valid `$_` variable it now contains the lines from gc. BTW the alias `sc` is/will be removed from PSv6 core versions. You could use `ForEach ($File in (gci "DirIn\*.csv")){(gc $File | select -Skip 2) | Set-Content "DirOut\$($File.Name)" }` –  Oct 12 '18 at 19:40
  • 1
    @LotPings: No: Since the `$_` in the `sc` command is referenced as part of an _argument_, it still refers to the `gci`-produced output object, so this is a viable solution, and generally preferable to using a `foreach` loop. Good point about `sc` in PowerShell _Core_. – mklement0 Oct 13 '18 at 02:26
  • To explain why this answer works: `$_` by itself would expand to each input file's _full path_, whereas only the _filename part_ is desired, which `$($_.Name)` provides. The surrounding `$(...)` is needed to embed an _expression_ such as `$_.Name` in a double-quoted string, because only simple variable references such as `$var` can be embedded directly - see [this answer](https://stackoverflow.com/a/40445998/45375) for details. – mklement0 Oct 13 '18 at 02:34