Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

@LINE function that supports OptionBase 1

Aug
2,838
149
Display line n from a text file,
or line n from the Windows Clipboard,
based on the value of Windows Environment Variable OptionBase

I wrote this PowerShell script for use with Take Command Console (TCC).

TCC uses option base 0 for many,
if not all,
of its functions.

I prefer the first line in a file to be line 1,
not line 0.

I have created a User-Defined Function to replace the internal @LINE function;

Code:
U:\>function line=`%@pshell["u:\line.ps1 %1 %2"]`

U:\>echo %@line[r:\zoom150.btm 1]
@SETLOCAL

I have to use a callback into TCC to obtain the clipboard contents,
as TCC only supports MTA with PowerShell,
not STA,
which Powershell requires when working with the Windows clipboard from TCC.
Code:
E:\Utils>unset optionbase

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 0"
@SETLOCAL

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 1"
@ECHO OFF

Code:
E:\Utils>set optionbase=0

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 0"
@SETLOCAL

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 1"
@ECHO OFF

Code:
E:\Utils>set optionbase=1

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 0"
EOF

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 1"
@SETLOCAL

Code:
E:\Utils>set optionbase=2

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 0"
OptionBase must be 0 or 1.

E:\Utils>pshell /s "u:\line.ps1 r:\zoom150.btm 1"
OptionBase must be 0 or 1.

Code:
E:\Utils>echo Line 1^nLine 2^nLine 3^nLine 4^nLine 5 > clip:

E:\Utils>type clip:
Line 1
Line 2
Line 3
Line 4
Line 5

E:\Utils>pshell /s "u:\line.ps1 clip: 1"
Line 1

E:\Utils>pshell /s "u:\line.ps1 clip: -0"
Line 5

The C# version that I created works,
and can also be used to create a replacement @LINE function.

Ref: @LINE function that supports OptionBase 1

Joe

Line.ps1
Code:
param(
    [Parameter(Position = 0)]
    [string] $Source,

    [Parameter(Position = 1)]
    [string] $NumberText
)

if ($args.Count -gt 0 -or $null -eq $Source -or $null -eq $NumberText) {
    [Console]::Error.WriteLine('Usage: LINES <file|CLIP:> <line-number>')
    exit 1
}

$optionBase = 0
$optionBaseValue = [Environment]::GetEnvironmentVariable('OptionBase')

if ($null -ne $optionBaseValue) {
    $parsedOptionBase = 0

    if (
        -not [int]::TryParse($optionBaseValue, [ref] $parsedOptionBase) -or
        ($parsedOptionBase -ne 0 -and $parsedOptionBase -ne 1)
    ) {
        [Console]::Error.WriteLine('OptionBase must be 0 or 1.')
        exit 1
    }

    $optionBase = $parsedOptionBase
}

$isNegative = $NumberText.StartsWith('-', [StringComparison]::Ordinal)
$magnitudeText = if ($isNegative) {
    $NumberText.Substring(1)
}
else {
    $NumberText
}

[long] $requestedNumber = 0
if (
    -not [long]::TryParse($magnitudeText, [ref] $requestedNumber) -or
    $requestedNumber -lt 0
) {
    [Console]::Error.WriteLine(
        'The line number must be a non-negative integer, optionally preceded by ''-''.'
    )
    exit 1
}

try {
    $lines = New-Object 'System.Collections.Generic.List[string]'

    if ([string]::Equals($Source, 'CLIP:', [StringComparison]::OrdinalIgnoreCase)) {
        $text = [TakeCommand.PowerShellHost]::InvokeCommand('type clip:')

        if ($null -eq $text) {
            [Console]::Error.WriteLine('The TCC callback returned no text.')
            exit 1
        }

        $reader = [System.IO.StringReader]::new([string] $text)
        try {
            while ($null -ne ($line = $reader.ReadLine())) {
                $lines.Add($line)
            }
        }
        finally {
            $reader.Dispose()
        }
    }
    else {
        $reader = [System.IO.StreamReader]::new(
            $Source,
            [System.Text.Encoding]::Default,
            $true
        )
        try {
            while ($null -ne ($line = $reader.ReadLine())) {
                $lines.Add($line)
            }
        }
        finally {
            $reader.Dispose()
        }
    }

    if ($isNegative) {
        $index = [long] $lines.Count - 1 - $requestedNumber
    }
    else {
        $index = $requestedNumber - $optionBase
    }

    if ($index -lt 0 -or $index -ge $lines.Count) {
        Write-Output 'EOF'
    }
    else {
        Write-Output $lines[[int] $index]
    }

    exit 0
}
catch {
    [Console]::Error.WriteLine($_.Exception.Message)
    exit 1
}

LineTestPS1.btm
Code:
@SETLOCAL
@ECHO OFF
if isdir r:\temp set temp=r:\temp

text > test.txt
Line 1
Line 2
Line 3
Line 4
Line 5
endtext

set theFile=test.txt
set LinePS1=u:\line.ps1

Gosub Test1 & rem with optionbase not defined.
Gosub Test2 & rem with optionbase=0 defined.
Gosub Test3 & rem with optionbase=1 defined.
Gosub Test4 & rem with optionbase=2 defined.

type %thefile > clip:

set theFile=clip:

echo Test using the contents of the Windows Clipboard.

Gosub Test1 & rem with optionbase not defined.
Gosub Test2 & rem with optionbase=0 defined.
Gosub Test3 & rem with optionbase=1 defined.
Gosub Test4 & rem with optionbase=2 defined.

ENDLOCAL
quit

:Test1
if defined optionbase unset optionbase

echo Running %_batchname with optionbase not defined.

echo First line in %theFile using Line.ps1 %theFile 0
pshell /s "%LinePS1 %theFile 0"
echo ^nLast line in %theFile using Line.ps1 %theFile -0
pshell /s "%LinePS1 %theFile -0"

pause
Return

:Test2
echo ^nRunning %_batchname with optionbase defined.
set optionbase=0
echo set optionbase=0

echo First line in %theFile using Line.ps1 %theFile 0
pshell /s "%LinePS1 %theFile 0"
echo ^nLast line in %theFile using Line.ps1 %theFile -0
pshell /s "%LinePS1 %theFile -0"

pause
Return

:Test3
echo ^nRunning %_batchname with optionbase defined.
set optionbase=1
echo set optionbase=1

echo First line in %theFile using Line.ps1 %theFile 0
pshell /s "%LinePS1 %theFile 0"
echo ^nFirst line in %theFile using Line.ps1 %theFile 1
pshell /s "%LinePS1 %theFile 1"
echo ^nLast line in %theFile using Line.ps1 %theFile -0
pshell /s "%LinePS1 %theFile -0"

pause
Return

:Test4
echo ^nRunning %_batchname with optionbase defined.
set optionbase=2
echo set optionbase=2

echo First line in %theFile using Line.ps1 %theFile 0
pshell /s "%LinePS1 %theFile 0"
echo ^nFirst line in %theFile using Line.ps1 %theFile 1
pshell /s "%LinePS1 %theFile 1"
echo ^nLast line in %theFile using Line.ps1 %theFile -0
pshell /s "%LinePS1 %theFile -0"

pause
Return

Joe
 
Last edited:
For those wondering...

Yes, I could have just done;

Code:
$line = Get-Content -Path "R:\somefile.txt" | Select-Object -Skip 4 -First 1

...to obtain, in this case, line 5, or;

Code:
$lines = Get-Content -Path "R:\somefile.txt"
$line  = $lines[4]

To get the last line of a text file;

Code:
$lastLine = Get-Content -Path "R:\somefile.txt" | Select-Object -Last 1

...but I prefer to use the StreamReader.

Joe
 
Or (may not be particularly robust) ..

Code:
function line1 `%@pshell[Get-Content -Path %1 -TotalCount %@eval[%2+1-%optionbase] | Select-Object -Last 1]`
 
Or (may not be particularly robust) ..

Code:
function line1 `%@pshell[Get-Content -Path %1 -TotalCount %@eval[%2+1-%optionbase] | Select-Object -Last 1]`
Code:
R:\>function line1
%@pshell[Get-Content -Path %1 -TotalCount %@eval[%2+1-%optionbase] | Select-Object -Last 1]

R:\>type clip:
Line 1
Line 2
Line 3
Line 4
Line 5

R:\>echo %@line1[clip: 1] 
PSHELL: The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot find drive. A drive with the name 'clip' does not exist.

Joe
 
For those that are curious,
such as myself,
here is a link to GetContentCommand.cs ,
which contains the GetContentCommand ,
which is Get-Content in PowerShell 5.1

GetContentCommand.cs is compiled into the Microsoft.PowerShell.Management module.

You can see this by running;

Code:
Get-Command -Module Microsoft.PowerShell.Management

On my Windows 10 system,
the module manifest,
Microsoft.PowerShell.Management.psd1
is located at;

C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Management\Microsoft.PowerShell.Management.psd1

Ref: PowerShell/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs at master · PowerShell/PowerShell

Joe
 
Back
Top