1

Following the thread here I am trying to write to an output file in PowerShell 7.2:

# [String] $csvFile = "output.csv"
[String] $csvFile = ".\output.csv"
$stream = New-Object IO.StreamWriter $csvFile, $true
$stream.WriteLine("Some, text")
$stream.Close()

The file is not created. Also, if I create the file beforehand, the script does not write to it. What am I missing? I can get it to work with Out-File but the streams are large and I prefer the Net approach.

Is it a permissions issue in the shell?

Omortis
  • 1,334
  • 19
  • 44
  • 2
    `System.IO.StreamWriter` is .Net, so you need to specify an absolute Full path and filename – Theo Jan 05 '22 at 15:36
  • 1
    .NET's working directory usually differs from PowerShell's, so you should always pass _full, file-system-native_ paths to .NET method calls. Use [`Convert-Path`](https://learn.microsoft.com/powershell/module/microsoft.powershell.management/convert-path) to convert a relative path to a full file-system-native one, assuming it already exists. To specify a file to be created in PowerShell's current location, use something like `"$PWD\file.txt"`, except from PS-specific drives. For more information, see [this answer](https://stackoverflow.com/a/57791227/45375) to the linked duplicate. – mklement0 Jan 05 '22 at 15:52

2 Answers2

3

My preferred approach:

# create a new file using the provider cmdlets
$newFile = New-Item -Name output.csv -ItemType File

try {
  # open a writable FileStream
  $fileStream = $newFile.OpenWrite()

  # create stream writer
  $streamWriter = [System.IO.StreamWriter]::new($fileStream)

  # write to stream
  $streamWriter.WriteLine("Some, text")
}
finally {
  # clean up
  $streamWriter.Dispose()
  $fileStream.Dispose()
}

For an existing file, use Get-Item or Get-ChildItem to find the existing file system item.

The advantage of letting the provider cmdlets deal with the file is that you don't need to worry about qualifying the relative path, .\output.csv will be resolves relative to the current location in the shell

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206
  • Nice and clean. Trying this now, as `StreamWriter` does not like the DFS URI I am trying to write to... Any chance you can edit this to use `Get-Item` instead. Porting from C# over to PowerShell... – Omortis Jan 05 '22 at 15:47
  • @Omortis If you provide it as a valid UNC path, eg. `\\domain\dfs-share\folder\file.txt`, then it shouldn't be a problem – Mathias R. Jessen Jan 05 '22 at 15:48
  • It shouldn't be, but it always is for me. Not sure why. I wonder if the `DFSroot$` in the URI is messing things up. – Omortis Jan 05 '22 at 15:49
  • 2
    @Omortis If the share name has `$` in it, make sure that you either escape it with a backtick, or enclose it in a non-expandable string literal. eg. ``$dfsPath = "\\domain\some`$stuff\..."`` or `$dfsPath = '\\domain\some$stuff'` – Mathias R. Jessen Jan 05 '22 at 15:51
  • 1
    As for getting the full, native path of an _existing_ item: The simplest solution is to use [`Convert-Path`](https://learn.microsoft.com/powershell/module/microsoft.powershell.management/convert-path). – mklement0 Jan 05 '22 at 15:54
  • 1
    @mklement0 kaboom! `Convert-Path` fixed the permissions issues, among other things. – Omortis Jan 05 '22 at 15:57
  • Similarly, if the file is to be created anyway, it is simpler to do `[System.IO.StreamWriter] (New-Item output.csv).FullName` (no need for a file stream). – mklement0 Jan 05 '22 at 15:59
0

You need to use an absolute path when using .Net classes.

Try

# get the full path for the output file
$fileOut = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($pwd.ProviderPath, "output.csv"))
$stream  = [System.IO.StreamWriter]::new($fileOut, $true)  # $true for Append
$stream.WriteLine("Some, text")
$stream.Dispose()
Theo
  • 57,719
  • 8
  • 24
  • 41