Multiple powershell scripts outputting to the same text file - powershell

I have a set of ps scripts, i will call them parents, that invoke other ps scripts, i will call them children. They all need to write to the same text file, i will call myoutfile.txt. The first output of the parent script should clear myoutputfile.txt. All subsequent child ps scripts should append to myoutputfile.txt, including another parent script later in the logic.
Using Out-File, the parent PS script opens and locks the file myoutputfile.txt. The children PS scripts need append to append to the file, but because the parent created the text file the children PS scripts fail silently to append to the text file.
I have tried Out-File and Add-Content. Add-Content puts out Chinese to the text file.

Either Out-File with append or Add-Content would work.
Unless you are actually trying to write Chinese, the encoding could be wrong.
$File ="PathTo\test.txt"
Get-Date | Out-File $File -Encoding ascii
"TEXT" | Out-File $File -Append -Encoding utf32
Add-Content -Path $File -Value "END" -Encoding Unicode
As you can see in the file, the result varies depending on encoding.
If you're getting Chinese, my guess is bytes.
Likely conversion problems earlier on.

It took a combo of Peter Schneider's comment and Martin van Delft's answer to get it working.

Related

How can we add new rows at the top of the CSV file using powershell script?

I am new to powershell scripting and I am looking for a way to add 2 new rows at the top of the already present csv file.
Things that I have tried is replacing the header and rows with the new rows.
I am looking for a way to add 2 new rows above the header in CSV.
You mention that you want to add the new lines above the header, which means that no CSV-specific processing is needed - it sounds like you're asking how to prepend lines to an existing text file (which happens to contain CSV - note that the resulting file will no longer be a valid CSV file).
E.g., assuming a target file named some.csv:
Note: Best to make a backup of the target file before trying these commands.
If the input file is small enough to fit into memory as a whole:
Reading the entire target file into memory as a single string with Get-Content -Raw allows for a convenient and concise solution:
Set-Content -LiteralPath some.csv -NoNewLine -Value (
#'
New line 1 above header
New line 2 above header
'# + (Get-Content -Raw some.csv)
)
Note that Set-Content applies a default character encoding (the active ANSI code page in Windows PowerShell, UTF-8 without BOM in PowerShell Core), irrespective of the current encoding of some.csv, so you may have to use the -Encoding parameter to specify the encoding explicitly.
Also note that the single-quoted here-string (#'<newline>...<newline>'#) uses the same newline style (CRLF (Windows-style) vs. LF (Unix-style)) as the enclosing script, which may not match the style used in some.csv - though PowerShell itself has no problem processing files with mixed newlines styles.
If the file is too large to fit into memory, use a streaming (line-by-line) approach:
$ErrorActionPreference = 'Stop'
# Create a temporary file and fill it with the 2 new lines.
$tempFile = [IO.Path]::GetTempFileName()
'New line 1 above header', 'New line 2 above header' | Set-Content $tempFile
# Then append the CSV file's lines one by one.
Get-Content some.csv | Add-Content $tempFile
# If that succeeded, replace the original file.
Move-Item -Force $tempFile some.csv
Note: Use of the Get-Content, Set-Content and Add-Content cmdlets is convenient, but slow; the next section shows a faster alternative.
If performance matters, use .NET types such as [IO.File] instead:
$ErrorActionPreference = 'Stop'
# Create a temporary file...
$tempFile = [IO.Path]::GetTempFileName()
# ... and fill it with the 2 new lines.
$streamWriter = [IO.File]::CreateText($tempFile)
foreach ($lineToPrepend in 'New line 1 above header', 'New line 2 above header') {
$streamWriter.WriteLine($lineToPrepend)
}
# Then append the CSV file's lines one by one.
foreach ($csvLine in [IO.File]::ReadLines((Convert-Path some.csv))) {
$streamWriter.WriteLine($csvLine)
}
$streamWriter.Dispose()
# If that succeeded, replace the original file.
Move-Item -Force $tempFile some.csv

How to remove the first 3 symbols in a text file?

How to remove the first 3 symbols in a text file with PowerShell and keep the file with the same name?
Just read the file using the Get-Content cmdlet, remove the file using a regex that replaces the first three characters with nothing and finally write it back using the Set-Content cmdlet:
(Get-Content 'yourfilePath.txt' -raw) -replace '^...' | Set-Content 'yourfilePath.txt'
Note: You probably want to specify the encoding using the -Encoding parameter when writing the content back to the file.

Out-File -append in Powershell does not produce a new line and breaks string into characters

I'm trying to understand some weird behaviour with this cmdlet.
If I use "Out-File -append Filename.txt" on a text file that I created and entered text into via the windows context menu, the string will append to the last line in that file as a series of space separated characters.
So:
"This is a test" | out-file -append textfile.txt
Will produce:
T h i s i s a t e s t
This wont happen if out-file creates the file, or if the text file has no text in it prior to appending. Why does this happen?
I will also note that repeating the command will just append in the same way to the same line. I guess it doesn't recognise newline or line break terminator or something due to changed encoding?
Out-File defaults to unicode encoding which is why you are seeing the behavior you are. Use -Encoding Ascii to change this behavior. In your case
Out-File -Encoding Ascii -append textfile.txt.
Add-Content uses Ascii and also appends by default.
"This is a test" | Add-Content textfile.txt.
As for the lack of newline: You did not send a newline so it will not write one to file.
Add-Content is default ASCII and add new line however Add-Content brings locked files issues too.

Powershell script write back to sources from drag and drop

I need to create a powershell script that removes quotes from CSV files in a user friendly drag and drop way. I have the basics of the script down courtesy of this page:
http://blogs.technet.com/b/heyscriptingguy/archive/2011/11/02/remove-unwanted-quotation-marks-from-csv-files-by-using-powershell.aspx
And I've already sucessfully made .ps1 files drag and droppable courtesy of this stack overflow question:
Drag and Drop to a Powershell script
The author of the answer implies that it's just as easy to drop a single file, many files, and folders with lots of files in them. However, I have yet to figure this out in a way that can also can write back to the source file. Here's my current code:
Param([string[]]$file)
(gc $file) | % {$_ -replace '"', ""} | out-file C:\Users\pfoster\Desktop\Output\test.txt -Fo -En ascii
Currently, this will only accept a single file, and output the result as a txt to a specified file regardless of the source file type (I can change that to CSV easily but I'd like the script to mirror the source). Ideally, I'd like it to accept files and folders, and to rewrite the source file. I have a feeling this would involve the get-ChildItem but I'm not sure how to implement that in the current scenario. I've also tried out-file $file and that didn't work either.
Thanks for the help!
For writing the modified content back to the original files try something like this:
foreach ($file in $ARGS) {
(Get-Content $file) -replace '"', '' | Out-File $file -Encoding ASCII -Force
}
Use a foreach in loop, because you need the file name in more than one place in the pipeline. Reading the content in a subshell and then piping the modified content into the Out-File cmdlet makes sure that the output file is only written after the content was already read.
Don't use a redirection operator ((Get-Content $file) >$file), because that would first open the file for writing (effectively truncating it) and afterwards read the content from the now empty file.
Beware that this approach may cause problems with large files, because each file is read completely into the RAM before they're processed and written back to disk. If a file doesn't fit into the available RAM the computer will start swapping, thus causing significant performance degradation.

Output ("echo") a variable to a text file

I'm running a PowerShell script against many servers, and it is logging output to a text file.
I'd like to capture the server the script is currently running on. So far I have:
$file = "\\server\share\file.txt"
$computername = $env:computername
$computername | Add-Content -Path $file
This last line adds question marks in the output file. Oops.
How do I output a variable to a text file in PowerShell?
The simplest Hello World example...
$hello = "Hello World"
$hello | Out-File c:\debug.txt
Note: The answer below is written from the perspective of Windows PowerShell.
However, it applies to the cross-platform PowerShell (Core) v6+ as well, except that the latter - commendably - consistently defaults to BOM-less UTF-8 as the character encoding, which is the most widely compatible one across platforms and cultures..
To complement bigtv's helpful answer helpful answer with a more concise alternative and background information:
# > $file is effectively the same as | Out-File $file
# Objects are written the same way they display in the console.
# Default character encoding is UTF-16LE (mostly 2 bytes per char.), with BOM.
# Use Out-File -Encoding <name> to change the encoding.
$env:computername > $file
# Set-Content calls .ToString() on each object to output.
# Default character encoding is "ANSI" (culture-specific, single-byte).
# Use Set-Content -Encoding <name> to change the encoding.
# Use Set-Content rather than Add-Content; the latter is for *appending* to a file.
$env:computername | Set-Content $file
When outputting to a text file, you have 2 fundamental choices that use different object representations and, in Windows PowerShell (as opposed to PowerShell Core), also employ different default character encodings:
Out-File (or >) / Out-File -Append (or >>):
Suitable for output objects of any type, because PowerShell's default output formatting is applied to the output objects.
In other words: you get the same output as when printing to the console.
The default encoding, which can be changed with the -Encoding parameter, is Unicode, which is UTF-16LE in which most characters are encoded as 2 bytes. The advantage of a Unicode encoding such as UTF-16LE is that it is a global alphabet, capable of encoding all characters from all human languages.
In PSv5.1+, you can change the encoding used by > and >>, via the $PSDefaultParameterValues preference variable, taking advantage of the fact that > and >> are now effectively aliases of Out-File and Out-File -Append. To change to UTF-8 (invariably with a BOM, in Windows PowerShell), for instance, use:
$PSDefaultParameterValues['Out-File:Encoding']='UTF8'
Set-Content / Add-Content:
For writing strings and instances of types known to have meaningful string representations, such as the .NET primitive data types (Booleans, integers, ...).
.psobject.ToString() method is called on each output object, which results in meaningless representations for types that don't explicitly implement a meaningful representation; [hashtable] instances are an example:
#{ one = 1 } | Set-Content t.txt writes literal System.Collections.Hashtable to t.txt, which is the result of #{ one = 1 }.ToString().
The default encoding, which can be changed with the -Encoding parameter, is Default, which is the system's active ANSI code page, i.e. the single-byte culture-specific legacy encoding for non-Unicode applications, which is most commonly Windows-1252.
Note that the documentation currently incorrectly claims that ASCII is the default encoding.
Note that Add-Content's purpose is to append content to an existing file, and it is only equivalent to Set-Content if the target file doesn't exist yet.
If the file exists and is nonempty, Add-Content tries to match the existing encoding.
Out-File / > / Set-Content / Add-Content all act culture-sensitively, i.e., they produce representations suitable for the current culture (locale), if available (though custom formatting data is free to define its own, culture-invariant representation - see Get-Help about_format.ps1xml).
This contrasts with PowerShell's string expansion (string interpolation in double-quoted strings), which is culture-invariant - see this answer of mine.
As for performance:
Since Set-Content doesn't have to apply default formatting to its input, it performs better, and therefore is the preferred choice if your input is composed of strings and/or of objects whose default stringification via the standard .NET .ToString() method is sufficient.
As for the OP's symptom with Add-Content:
Since $env:COMPUTERNAME cannot contain non-ASCII characters (or verbatim ? characters), Add-Content's addition to the file should not result in ? characters, and the likeliest explanation is that the ? instances were part of the preexisting content in output file $file, which Add-Content appended to.
After some trial and error, I found that
$computername = $env:computername
works to get a computer name, but sending $computername to a file via Add-Content doesn't work.
I also tried $computername.Value.
Instead, if I use
$computername = get-content env:computername
I can send it to a text file using
$computername | Out-File $file
Your sample code seems to be OK. Thus, the root problem needs to be dug up somehow. Let's eliminate chance for typos in the script. First off, make sure you put Set-Strictmode -Version 2.0 in the beginning of your script. This will help you to catch misspelled variable names. Like so,
# Test.ps1
set-strictmode -version 2.0 # Comment this line and no error will be reported.
$foo = "bar"
set-content -path ./test.txt -value $fo # Error! Should be "$foo"
PS C:\temp> .\test.ps1
The variable '$fo' cannot be retrieved because it has not been set.
At C:\temp\test.ps1:3 char:40
+ set-content -path ./test.txt -value $fo <<<<
+ CategoryInfo : InvalidOperation: (fo:Token) [], RuntimeException
+ FullyQualifiedErrorId : VariableIsUndefined
The next part about question marks sounds like you have a problem with Unicode. What's the output when you type the file with Powershell like so,
$file = "\\server\share\file.txt"
cat $file
Here is an easy one:
$myVar > "c:\myfilepath\myfilename.myextension"
You can also try:
Get-content "c:\someOtherPath\someOtherFile.myextension" > "c:\myfilepath\myfilename.myextension"