Show script line number in write-host statements along with literal strings and vars

I am trying to create a function that I can use in write-host statements along with string literals and variables, to insert the words "Line No. " followed by the line number, followed by ": " into the beginning of write-host statements that show the values held by variables, e.g.:

```
Line No. 5: The variable 'this_script_fullN' = "E:\Apps\UtilPS\Showing_the_script_line_number_line_number_lineno_fnLn_in_a_debug_message_aaa.ps1"
```

I found a function that I can use to insert in write-host statements the line number (alas, without the “Line No.” prefix and the colon-space [": "] suffix following the line number).

But I don’t understand why the function reference in the write-host statements [1] does not show the line number on lines 6 and 7, but [2] does show the line number on lines 5, 9 and 13 of the following script:

function fnLN {
    $MyInvocation.ScriptLineNumber
}

write-host "Line No. $(fnLN): Script Failed"
write-host "Line No. ${fnLN}: "
write-host "`n`t" "Line No. ${fnLN}"
$this_script_fullN = $MyInvocation.MyCommand.Path
write-host "Line No. $(fnLN): `$this_script_fullN = ${this_script_fullN}"

try { Bad-Command }
catch {
write-host "Line No. $(fnLN) Script Failed"
exit
}

I would appreciate any suggestions, especially if you can refer me to link(s) that would help me understand what I am missing.

I am very appreciative of your time and your generous efforts.
Marc

What is it what you’re actually trying to do? That looks very weird to me.

$(fnLN) ← This is the so called subexpression operator. Whatever is inside is evaluated and outputted. In this case the function fnLN

Here you can read more about:

${fnLN} ← This is the syntax to output a variable with the name foo. Since you don’t have a variable with the name of “fnLN” you don’t get any output in your case. :man_shrugging:t3:

Without getting whatfor you need this you may take a look a the -f format operator

I have the feeling that this is an XY problem. Would you like to tell us what you’re actually want to achieve?

$Error automatically tracks generated errors. New errors are push on to the top so the newest error is always $Error[0] which contains $Error[0].invocationInfo.ScriptLineNumber

I apologize and I am truly sorry for being obtuse or confusing, and for my tardiness in replying to your kind efforts to educate me.
I’m replying as quickly as possible now, and I will study the links you sent me immediately thereafter.

Assume I have a write-host statement (e.g., “write-host hello”) on line 5 of my PS script.
When I print that statement to console, I want the following to appear, “Line no. 5: Hello”

The following code shows how I handle this in Python:

    import inspect # https://docs.python.org/3/library/inspect.html
    from inspect import currentframe, getframeinfo
    
    # Here I print the Line no. information w/o using a function.
    print(f'Line no. {str(inspect.currentframe().f_lineno)}:  Hello')
    
    def fnLn(line_no="Line no. ", Colon=':', spaces=2):
        #Returns   'Line no. = ' + [lineno] + ': '
        spc = ' '*spaces
        ret_val = f"{line_no}{str(inspect.currentframe().f_back.f_lineno)}{Colon}{spc}"
    
        return ret_val
    
    # Here I print Line no. info using a function, and its much shorter.
    print(f'{fnLn()}Hello')

The Python code above prints the following to console:

     Line no. 5:  Hello
     Line no. 15:  Hello

Thank you again for your kind efforts to help me.
Marc

You’re telling what you want to do but you’re not telling why … whatfor … what’s the bigger picture? :thinking: There might be a better way in PowerShell. You may keep in mind that PowerShell was not made for programmers or software developers. It was made for administrators. :wink:

And … since you’ve already found a function actually doing what you need, why don’t you use it?

That would only help for people knowing Python as well. I don’t, unfortunately. :man_shrugging:t3:

Edit:

BTW: You don’t even need to use Write-Host to produce a console output. Everything you drop inside of a script will be outputted to the default output stream.

function fnLN {
    $MyInvocation.ScriptLineNumber
}

'Line no. {0}: Hello' -f $(fnLN)
"Line no. $(fnLN): Hello"
"Line no. " + $(fnLN) + ": Hello"

You said,

“You’re telling what you want to do but you’re not telling why … whatfor … what’s the bigger picture? :thinking: There might be a better way in PowerShell. You may keep in mind that PowerShell was not made for programmers or software developers. It was made for administrators.”

To be courteous to you, I tried to say less, but now I see I should have explained more. I am not a professional programmer or software developer, even though I am trying to learn to program Python for both system administration and database purposes. I am also trying to learn Powershell (“PS”) for system administration purposes.

When I was trying to debug a VBScript or VBA program, I would show the values of various vars using the messagebox method at critical points. In Python, I do something similar to facilitate debugging by making a practice of printing the values of vars to the console (e.g., “print('varname = ’ + str(varname))”), at every step where I modify the var. When I have finished debugging the Python script, I remove or comment out those print statements.

I had assumed incorrectly that, to facilitate debugging a PS script, I should use write-host statements instead of the Python print statements.

Thank you for:
1. Disabusing me of that delusion, and a lot of other good education; and
2. Letting me know that the function object, fnLn, in PS is not a variable, and that is why my attempt to use the syntax (which would be appropriate for a variable) of “${fnLn}” does not send anything to the default output stream.

I am mildly Aspergers, and I need to understand something well to be comfortable using it. For my own educational and debugging purposes, I like to print to console (i.e., send to the default output stream) a text string such as the following:
[In Vbscript or VB or VBA or Python:] Line No. X: 'varname = ' + str(varname)

If you show me a better way of seeing what the values of vars are at various points in a PS script, for debugging purposes, I will be grateful and use whatever (better) method you show me.

But regardless of whatever new alternative approach you show me, my curiousity and Aspergers virtually require me to learn how to print to console (i.e., send to the default output stream) a text string (similar to what I use in VBScript, VB, VBA and Python), i.e.:
Line No. X: 'varname = ' + str(varname)

I want to know how to print that statement to console (i.e., send it to the default output stream) even if there is no reason for me to do that anymore because (hypothetically) you show me some debugging methodology that makes it a waste of my time and the computer’s time to print that statement to console.

I do not know what the proper PS notation would be, so please forgive me for using bracketed pseudocode to indicate the return value that I would like the ‘fnLn’ function to somehow send to the default output stream along with "$varname = ${varname}"as follows: [Line No. X:] "$varname = ${varname}"

Thank you again for your time and especially for your effort.
Marc

The least of us are … since PowerShell is made for administrators most of us are system engineers or consultants, some technicians or just enthusiasts. :man_shrugging:t3: :wink:

That’s a good choice in my opininion. :+1:t3:

Of course you can output anything you want with Write-Host but why does it have to have the line number in it? :thinking:

What a hassle. :scream::wink:

The better approach in PowerShell would be to use

and

You can switch on and off their messages either with the according switch parameter for built in or even own cmdlets or with the according preference variables. This way you can leave them where there are but get rid of their output when you donn’t need it anymore.

Most PowerShell coders use VSCode for PowerShell developement. There you have a lot of built in help. Including breakpoints and debug mode. I do not use very often myself but if you want to know the value of a certain variable at a time it is quite handy.

You may read about or watch a video on Youtube about it.

Hello,
Supposing you are OK to send your output to a file, and then display it with the line numbers, you can do that:

forEach($line in Get-Content Process2023-11-06){$i++;"line n° $i" + $line;}

Otherwise, I imagine you have to save the number somewhere, perhaps in a file, but then it can be somewhat heavy.

In PowerShell, when you write the name of a variable alone on a line of script, that displays its value on the console (at execution, naturally).
If you want a line number in front of it, you can write it in the code, but it will not be updated when you insert or suppress lines.
If you want to execute step by step, you have to use PowerShell ISE, that you will find in the start menu of Windows.
Then, you can put breakpoints, and starting from a breakpoint execute step by step with the F11 key.
During the stop at a breakpoint, you can get the value of a variable by positioning the mouse cursor on it.

1 Like

Oh, I take note of this.

I want the line number even if it’s a hassle. But it is not a hassle to get the line number by itself, without the prefix "Line No. " and the suffix ": "; I found the solution totally by accident. I just needed to replace the curly brackets with parens. Here is a revised version of my Nov. 2 original posting. It includes an additional function, fnLn1, which in turn includes a return statement, that produces unwanted duplication in the output:

function fnLN {
    $MyInvocation.ScriptLineNumber
    #Write-Output $MyInvocation
}

cls
write-host "[Using fnLn] Line No. $(fnLN): and again, this is Line No. $(fnLn): "
write-host "Line No. $(fnLN): [Using fnLn] and again, this is Line No. $(fnLn): "
write-host "$(fnLN) The case of the letter 'N' in these parenthetical expressions does not seem to matter since `$(fnLn) = $(fnLn), and `$(fnLN) = $(fnLN) "

The code above produces the following in the console:

[Using fnLn] Line No. 7: and again, this is Line No. 7: 
Line No. 8: [Using fnLn] and again, this is Line No. 8: 
9 The case of the letter 'N' in these parenthetical expressions does not seem to matter since $(fnLn) = 9, and $(fnLN) = 9 
PS E:\Apps\UtilPS> 

As I said above, it was a dumb luck typo that solved part of my problem (making the line number appear in the write-host statement).

My next challenge is to figure out how to create a PS function ‘fnLn’ (shorthand for function-Line-number) that I could use in write-host statements (which I make to facilitate debugging), to show the line number as follows: write-host [pseudocode: fnLn] "$(var_a) = $(var_a)"`, so that the output to console (if the statement were on line no. 5, and var_a were a string variable having the value of “ABC”) would be “Line No. 5: $(var_a) = ABC”

A lot of people like having the line number appear for debugging purposes. For example:

  1. https://clan8blog.wordpress.com/2015/03/30/showing-the-script-line-number-in-a-debug-message/ I confess I am unable to consistently use the code he offered on that page. I say that I am unable to use his code successfully consistently since the write-host statements in my posting on November 2 did not print to the console (i.e., go to the default output stream) the line numbers on lines 6 and 7, but did show the line number on lines 5, 9 and 13. I do not understand why some were successful even though I was using write-host to print to console output from a function.

  2. https://github.com/PowerShell/PowerShell/issues/10972 This github.com posting is way above my pay-grade/competence at this point in time, but it does show that people other than me also want the line number for debugging purposes.

  3. [How to display line numbers where Powershell exceptions are raised - Stack Overflow)] This StackOverflow posting, using write-debug, too is way above the Beginner level, and I think it would pause the execution of the scripts, which I do not want.

  4. https://arcanecode.com/2021/10/04/fun-with-powershell-write-debug/ If you go down about 2/3 of the page, you’ll find some code (which I tried to reproduce below) that somehow prints the line number of the statement being executed, but I cannot figure out what in the code (which I am quoting below) teases out what I think is the line number, 30. The following is the code that you can find if you go down about 2/3 of the page:

As usual, re-run the function to refresh the version in memory, then call Show-FileInfo again.
    
        1 Get-ChildItem -Path 'C:\Demo' | Show-FileInfo -Debug
    
    Result:
    
     1  DEBUG:
     2    Function.......: Show-FileInfo
     3    File Name......: C:\Demo\05 - Arrays and Hashtables.ps1
     4    File Length....: 8,486 bytes.
     5    DebugPreference: Stop
     6    PS Version.....: 7.1.4
     7  Write-Debug:
     8  Line |
     9    30 |      Write-Debug -Message $dbgMsg
    10       |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    11       | The running command stopped because the preference variable "DebugPreference" or common parameter is set to Stop:
    12    Function.......: Show-FileInfo
    13    File Name......: C:\Demo\05 - Arrays and Hashtables.ps1
    14    File Length....: 8,486 bytes.
    15    DebugPreference: Stop
    16    PS Version.....: 7.1.4

Do you actually read my replies? That’s what I explained in my very first answer. :man_shrugging:t3:

PowerShell in general is case insensitive. So it does not matter if you write it FnLn or fnLN or fNlN. It is the same. :man_shrugging:t3:
So the code you wrote outputs the same thing twice.

Again - you may read the answers you get more carefully or you should ask more specific questions.

Why is that a challenge? What is it what you’re still missing?

"Line No. $(fnLn): `$(var_a) = $var_a"

If I need to output a variable during the development I’m used to use single quotes around variables to emphasize if there are maybe unwanted spaces around values I wasn’t aware of before. So it would look like this:

"Line No. '$(fnLn)': `$(var_a) = '$var_a'"

I have the impression that you focus way too much on the debugging of your code than on the code itself. It’s like you want learn how to build a car engine from scratch when you actually just want to commute to work. While it does not bother of course it does not serve any benefit actually and it does not improve the commute. :man_shrugging:t3:
I’d recommend using the debugging help you get from VSCode.

I am embarrassed. Not your fault. Entirely MY fault.
I did read your replies, a few times, each of them.
I apologize for the fact that some things did not sink into my brain. I don’t know why I missed that. I’m sorry.

Please forgive me for trying to answer by providing you with yet another version of my coding, and the output to console, which shows what I am trying to LEARN how to achieve.

Here’s the latest version of my coding:

function fnLN {
    $MyInvocation.ScriptLineNumber
    #Write-Output $MyInvocation
}

cls
write-host "[Using fnLn] Line No. $(fnLN): and again, this is Line No. $(fnLn): "
write-host "Line No. $(fnLN): [Using fnLn] and again, this is Line No. $(fnLn): "
write-host "$(fnLN) The case of the letter 'N' in these parenthetical expressions does not seem to matter since `$(fnLn) = $(fnLn), and `$(fnLN) = $(fnLN) "
[String]$varA = "ABC"
write-host "$varA"
write-host "I want to modify fnLn so that the bracketed statement: [write-host `"fnLn `$`(varA) = `$varA`"] will cause the following to appear in the console: Line No. 12: `$(varA) = $varA"

Here’s the output:

[Using fnLn] Line No. 7: and again, this is Line No. 7: 
Line No. 8: [Using fnLn] and again, this is Line No. 8: 
9 The case of the letter 'N' in these parenthetical expressions does not seem to matter since $(fnLn) = 9, and $(fnLN) = 9 
ABC
I want to modify fnLn so that the bracketed statement: [write-host "fnLn $(varA) = $varA"] will cause the following to appear in the console: Line No. 12: $(varA) = ABC
PS E:\Apps\UtilPS>

This weekend I will re-read (a few times) everything you told me, and I will try again to learn how to use Visual Studio Code to code with Powershell. I am sure you are right that that will teach me probably all I need to learn. My son, a professional programmer has been counseling me to use Visual Studio Code to code with Python, but I had a lot of trouble figuring out how to use Visual Studio Code. I’ll work on it this weekend. Thank you again. Marc

Seems like you prefer to learn it the hard way. :stuck_out_tongue_winking_eye: :smiley: :love_you_gesture:t3:

That’s actually confusing me a lot. Did you try my suggestion? Did it fit your requirement?

That looks weird to me. How about NOT using code to try to get the output you want. Try to write an example of the desired ouput (still formatted as code please). This way it may be easier to get what you expect it to look like.

I’m curious - what do you use until now to develop PowerShell code?

Eventually, I realized how to create the solution by using the information you kindly gave me in your very first reply.

Re-reading the page about Operators, and thinking out it late at night, enabled me to craft the following solution:

function fnLn {
   $LineNo = $MyInvocation.ScriptLineNumber
   $retval = "Line No. ${LineNo}: "
   return $retval
}

cls
$varA = 'ABC'
write-host "$(fnLn)`$(varA) = ${varA}"

The code above sent to the console (in the Windows PS ISE) exactly what I wanted:

Line No. 9: $(varA) = ABC

I acknowledge that what I wanted sounded weird to you, and you’re probably right that:

  1. I “focus way too much on the debugging of your code than on the code itself. It’s like you want learn how to build a car engine from scratch when you actually just want to commute to work. While it does not bother of course it does not serve any benefit actually and it does not improve the commute.” and
  2. “Seems like you prefer to learn it the hard way.”

But I want you to know that I did read your replies, a few times, each of them; and I apologize for the fact that it took a long time for me to realize that the solution to my question was indeed embedded in the info you gave me in your very first reply.

I do not know why I missed that. I’m embarrassed and I am sorry for wasting so much of the time you selflessly gave to help me. Without your help, I wouldn’t have found the solution.

I strongly disagree. If it’s not me it’s someone else. :man_shrugging:t3: But I’m glad to hear that you’ve finally achieved what you wanted. :+1:t3: :love_you_gesture:t3:

1 Like