The following script works well when the filename is specified on the command line.
tail.bat@echo off
set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"
powershell "Get-Content %FILENAME% -Last %COUNT%"
However, what I need is to be able to pipe the text into Get-Content
from stdin. I would like to write the following to get the last three Subversion tags assigned to the project. What can I do to get the source to Get-Content
to be stdin?
svn ls svn://ahost/arepo/aproject/tags | call tail.bat -3
NB: I am not permitted to install any helpful tools like tail
from the outside. It has to be done with the programs already available on the machine.
Update:
@mklement0 provided the answer. From that, I added code to use a default COUNT value of 10 if it is not provided. This matches the UNIX/Linux way.
@echo off
SET "COUNT=%~1"
IF "%COUNT:~0,1%" == "-" (
SET "COUNT=%COUNT:~1%"
SHIFT
) ELSE (
SET "COUNT=10"
)
SET "FILENAME=%~1"
if "%FILENAME%" == "" (
powershell -noprofile -command "$Input | Select-Object -Last %COUNT%"
) else (
powershell -noprofile -command "Get-Content \"%FILENAME%\" -Last %COUNT%"
)
EXIT /B
Use the Read-Host to Prompt for User Input in PowerShell The -Prompt parameter is used to specify a text to provide the information about what to input. It appends a colon : to the text you enter. The Read-Host pauses execution and receives input. When a user enters the value at the prompt, it returns that same value.
The Read-Host cmdlet reads a line of input from the console (stdin). You can use it to prompt a user for input. Because you can save the input as a secure string, you can use this cmdlet to prompt users for secure data, such as passwords. Note. Read-Host has a limit of 1022 characters it can accept as input from a user ...
There are a couple of ways to write the output of PowerShell to a file. The most common ways are to use the Out-File cmdlet or the redirection operator > . Other options are to use the Set-Content and Add-Content cmdlet.
The Get-Content cmdlet gets the content of the item at the location specified by the path, such as the text in a file or the content of a function. For files, the content is read one line at a time and returns a collection of objects, each of which represents a line of content.
Rewrite tail.bat
as follows:
@echo off
set "COUNT=%1"
set "COUNT=%COUNT:-=%"
set "FILENAME=%~2"
if "%FILENAME%"=="" (
powershell -noprofile -command "$Input | Select-Object -Last %COUNT%"
) else (
powershell -noprofile -command "Get-Content \"%FILENAME%\" -Last %COUNT%"
)
This will make the PowerShell CLI read stdin input via the automatic $input
variable, if no filename argument was passed, courtesy of this answer.
Example:
C:> (echo one & echo two & echo three) | tail.bat -2
two
three
Note:
While PowerShell generally sends through the pipeline and outputs objects of any kind, its interface to the outside world invariably involves strings.
Thus, given that $Input
is an enumerator that represents outside stdin input, we can be sure that it enumerates the input text lines (as strings) one by one, so all we need is to select the lines of interest, which is why piping to Select-Object
is sufficient.
By contrast, reading a file by name in PowerShell requires Get-Content
(which, incidentally, also sends the input file's lines one by one through the pipeline, unless you also specify -Raw
); since Get-Content
has tail
functionality built in, via parameter -Tail
(and its alias -Last
), it is all that is needed here.
CAVEAT: Character decoding on input and re-encoding on output is involved when PowerShell talks to the outside world:
If you're only ever dealing with ASCII-encoded input (single-byte characters with code points ranging between 0 - 127), you needn't worry.
Otherwise, prepare for a world of pain - see below for details.
Character decoding/re-encoding issues:
Assuming that PowerShell recognizes your input encoding (see below), the output encoding is invariably what the console window's assigned encoding is; by default, unfortunately, that is the OEM codepage (e.g., the "DOS" code page CP437 on US-English systems), reflected in PS as [Console]::OutputEncoding
.
Thus, with properly recognized input, if you print to the console, things will look OK, but if you capture the output in a file, you'll end up with an OEM-codepage-encoded file, which is probably undesired.
If feasible, you could fundamentally set up your console windows to use your codepage (input and output encoding) of choice (using chcp
), but trying to change the encoding ad-hoc in your script is, unfortunately, not an option.
Note that using UTF-8 - codepage 65001
- only works if you configure your console windows to use one of the TT (TrueType) fonts.
As written above, the set of input encodings that are properly recognized is unfortunately limited to the following, based on the default input encoding (which is also the OEM codepage, reflected in PS as [Console]::InputEncoding
; remember: input will be re-encoded on output):
Unicode
, subject to re-encoding to something potentially different on output)You could hard-code an expected input encoding by adding -Encoding <enc>
to the Get-Content
call (which expects the Windows default codepage encoding by default), but to do the same for stdin input (as reflected in $Input
) would be non-trivial.
[Console]::OutputEncoding
encoding is applied):powershell -noprofile -command "$Input | % { [text.encoding]::utf8.GetString([Console]::InputEncoding.GetBytes($_)) } | Select-Object -Last %COUNT%"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With