Why is my text file incomplete after using StreamWriter object? - powershell

I wrote a small program which generates big text files. I found using a StreamWriter is much, much faster than other methods I know. But the end of each text file is missing.
I reduced the program to a very simple snippet to spot the problem, but I'm still unable to understand how to solve it.
#$stream = [System.IO.StreamWriter] "test.txt"
# also tested with $stream = New-Object System.IO.StreamWriter("test.txt")
$i = 1
while($i -le 500) {
$stream.WriteLine("$i xxxxxx")
$i++
}
$stream.flush # flushing don't change anything
$stream.close # also tested with $stream.dispose
exit 0
Problem 1:
The end of the file is missing. Depending of line length, the last line is around 495, generaly cut in the middle of the line.
Problem 2:
When the program is finished, the text file is still locked (we can read it, but not delete/rename). We have to exit from PowerShell to gain full access to the file.
Tested on Windows 2003 and Windows 2008 with exact same result.
EDIT
dugas found the problem: I forgotten some parenthesis. Which solve the problem show with my code snippet.
But my original program have the parenthesis. So I mark this question as solved, and will open a new one when I'll found a better snippet for this specific problem.
EDIT 2
Got it. I had a hidden exception. Many thanks !

You are missing parenthesis when calling the StreamWriter's methods:
Change:
$stream.close
to
$stream.Close()
You may also want to wrap your StreamWriter in a try/finally and call Dispose on it in the finally:
try
{
$stream = [System.IO.StreamWriter] "C:\Users\168357\Documents\test2.txt"
$stream.WriteLine("xxxxxx")
}
finally
{
if ($stream -ne $NULL)
{
$stream.Dispose()
}
}

Related

Powershell: stripping a file name sometimes doesn't work

I have a Powershell script that is called from the command line.
script.ps1 "\\testfolder" "testinput" "xml" "xml2html.xsl" "testfile" "css"
The script uses these command line arguments:
param([string]$publish_folder, [string]$input_filename, [string]$input_ext, [string]$transformation_filename, [string]$output_filename, [string]$output_ext)
$input_filename and $output_filename may be a full path+filename, the filename only or the filename without extension.
$inputFileNameOnly = [System.IO.Path]::GetFileNameWithoutExtension($input_filename)
$inputPath=$publish_folder+"\"+$inputFileNameOnly+"."+$input_ext
$outputFileNameOnly = [System.IO.Path]::GetFileNameWithoutExtension($output_filename)
$outputPath=$publish_folder+"\"+$outputFileNameOnly+"."+$output_ext
When I run this locally, it works. Output path:
\\testfolder\testfile.css
When I run the same script in an AWS instance, it fails. $inputpath is calculated correctly, but Output path becomes:
\\testfolder\.
so both $output_filename and $output_ext are empty.
The paths are longer than \\testfolder\, but not long enough to cause trouble (about 150 characters). Total length of the arguments doesn't seem to be a problem either.
What could be causing this problem?
$outputPath="$publish_folder+"\"+
Looks like you broke your concatenation here. Double-quote before $publish_folder.
Edit:
Can also be written like this if you don't want to concatenate (+)
$outputPath="$publish_folder\$outputFileNameOnly.$output_ext"
Anything in double-quotes should be expanded correctly.
I set up your script as a function and passed those parameters.
function rename {
param(
[string]$publish_folder,
[string]$input_filename,
[string]$input_ext,
[string]$transformation_filename,
[string]$output_filename,
[string]$output_ext
)
$inputFileNameOnly = [System.IO.Path]::GetFileNameWithoutExtension($input_filename)
$inputPath = $publish_folder+"\"+$inputFileNameOnly+"."+$input_ext
$outputFileNameOnly = [System.IO.Path]::GetFileNameWithoutExtension($output_filename)
$outputPath = $publish_folder+"\"+$outputFileNameOnly+"."+$output_ext
return $outputPath
}
Output is this...
\\\\testfolder\\\\testfile.css
Try passing $publish_folder without ending backslash
Edit: Sorry about formatting. Need some forum practice. =)
With some more testing, we found the cause of the problem.
$outputFileNameOnly = [System.IO.Path]::GetFileNameWithoutExtension($output_filename)
GetFileNameWithoutExtension fails (in the AWS instance) when $output_filename does not contain an extension. On my local machine this worked correctly.
So we added an extension to the command line argument and the script works correctly.

How to use custom PowerShell functions?

Question about running a custom function in Powershell.
I'm on Windows 10 and I'd like to somehow print my monorepository's directory tree structure excluding node_modules. This is not supported out of the box but requires a custom function to be defined. I found one solution on StackOverflow (https://stackoverflow.com/a/43810460/9654273), which would enable using a command like:
tree -Exclude node_modules -Ascii > tree.txt
The problem is I don't know what to do with the provided source code :D The answer says "add to your $PROFILE, for instance", so I ran notepad $PROFILE in PowerShell, pasted the code snippet there, saved it and tried running the command. It didn't work because I did something wrong. According to the StackOverflow post's comments from anand_v.singh and mklement0 I was still running some other tree command, not the one I just attempted to define.
So how do I use a custom function in PowerShell? Starting point is that source code is on StackOverflow and I don't know where to paste it. Or do you know some other, easier way to print a directory tree on Windows 10 excluding node_modules?
I had the same problem with that function. The issue is the special characters in the hashtable at line 106:
$chars = #{
interior = ('├', '+')[$ndx]
last = ('└', '\')[$ndx] #'
hline = ('─', '-')[$ndx]
vline = ('│', '|')[$ndx]
space = ' '
}
I changed the special characters to ascii as follows:
$chars = #{
interior = ('+', '+')[$ndx]
last = ('\', '\')[$ndx] #'
hline = ('-', '-')[$ndx]
vline = ('|', '|')[$ndx]
space = ' '
}
The only downside is that you do not now have the option of using special graphics characters (the Ascii switch is still there, but does nothing). Maybe someone could tell us how to embed them properly.

In IF / ELSE evaluation, which code runs faster: large block of code in IF, or large block in ELSE?

In Powershell, I have a script that does something if a file exists, and shows a message if it doesn't. I can write it in two ways:
IF (Test-Path $path_to_file) {run some elaborate code}
ELSE {write-host "File doesn't exist"}
Or I can do:
IF ((Test-Path $path_to_file) -eq $FALSE) {write-host "File doesn't exist"}
ELSE {run elaborate code}
These two do exactly the same thing. Will there be any difference in processing speed (assuming the 'elaborate code' is a long piece of code that parses long text files)?
The if/else statement executes once regardless, and should not affect the runtime of any code afterwards.
if (x) { <#short#> } else {
#Long
}
This is generally more readable if it's a single line, you don't end up looking for where the else connects at the end of a long block of code when trying to read it back.
if there is any performance change it will likely be so minor that the readability of the above statement is magnitudes more important.
over 10000 iterations of 'meaningful' code, vs printing a string, the difference between if the code was in the If or Else was negligible (avg 0.038s) and not reliably skewed to one result, I'd say you're safe to not even think about performance when considering this issue.

Read entire line $nick found on

Below I have wrote my basic goal and the code I already have, any help is much appreciated as I am learning myself how IRC Scripting works, thanks guys!
on $*:text:*test*:#: {
if ($date isin $read(test1.txt, 1)) {
if ($nick isin $read(test1.txt, 1)) { write test.txt "entire line $nick was found on in test1.txt" $1- }
}
}
In the future, you should make your question clearer.
Your question looks like this one mIRC Search for multiple words in text file, you can read my answer there for more information, it's mostly the same so I'm copying and pasting it here with edits for your case.
To read a .txt file line by line you need a loop. To use this loop type: /findNick <NICK>
alias findNick {
var %nick = $1
while ($read(test1.txt, nw, $+(*,$date,*), $calc($readn + 1))) {
var %line = $v1
if (%nick isin %line) {
echo -a %nick found on the line: %line
; do your stuff here
}
}
}
$readn is an identifier that returns the line that $read() matched. It is used to start searching for the pattern on the next line. Which is in this case $date. The asteriks means a wildcard, so anything that contains that date.
In the code above, $readn starts at 0. We use $calc() to start at line 1. Every match $read() will start searching on the next line. When no more matches are after the line specified $read will return $null - terminating the loop.
The w switch is used to use a wildcard in your search
The n switch prevents evaluating the text it reads as if it was mSL code. In almost EVERY case you must use the n switch. Except if you really need it. Improper use of the $read() identifier without the 'n' switch could leave your script highly vulnerable.
The result is stored in a variable named %line to use it again to check wheter $nick is in the found line. If the $nick was found, it will echo the result in your active window.
And again, if there's anything unclear, I will try to explain it better.

powershell remove first characters of line

I am parsing a JBOSS log file using powershell.
A typical line would being like this :
2011-12-08 09:01:07,636 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].etc..
I want to remove all the characters from character 1 until the word ERROR. So I want to remove the date and time, the coma and the number right after it. I want my lines to begin with the word ERROR and delete everything before that.
I looked on google and tried different things I have found but I struggle and can't make it work. I tried with substring and replace but can't find how to delete all characters until the word ERROR.
Any help would be greatly appreciated,
Thanks a lot!
This one-liner will read the contents of your file (in the example jboss.txt) and replace every line containing ERROR by ERROR + whatever follows on that line. Finally it will save the result in processed_jboss.txt
get-content jboss.txt | foreach-object {$_ -replace "^.*?(ERROR.*)",'$1'} | out-file processed_jboss.txt
Assuming the log line is in a variable of type string this should do it:
$line = "2011-12-08 09:01:07,636 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].etc.."
$ErrorIndex = $line.indexof("Error",0)
$CleanLogLine = $Line.Substring($ErrorIndex, $line.length)
Reference:
http://msdn.microsoft.com/en-us/library/system.string.aspx