Question:
I need to only search the 1st line and last line in a text file to find a “-” and remove it.
How can I do it?
I tried select-string, but I don’t know to find the 1st and last line and only remove “-” from there.
Here is what the text file looks like:
1 2 3 4 5 6 7 8 |
% 01-A247M15 G70 N0001 G30 G17 X-100 Y-100 Z0 N0002 G31 G90 X100 Y100 Z45 N0003 ; --PART NO.: NC-HON.PHX01.COVER-SHOE.DET-1000.050 N0004 ; --TOOL: 8.55 X .3937 N0005 ; N0006 % 01-A247M15 G70 |
Something like this?
1 2 3 4 |
$1 = Get-Content C:\work\test\01.I $1 | select-object -index 0, ($1.count-1) |
Answer:
Ok, so after looking at this for a while, I decided there had to be a way to do this with a one liner. Here it is:
1 2 |
(gc "c:\myfile.txt") | % -Begin {$test = (gc "c:\myfile.txt" | select -first 1 -last 1)} -Process {if ( $_ -eq $test[0] -or $_ -eq $test[-1] ) { $_ -replace "-" } else { $_ }} | Set-Content "c:\myfile.txt" |
Here is a breakdown of what this is doing:
First, the aliases for those now familiar. I only put them in because the command is long enough as it is, so this helps keep things manageable:
gc
meansGet-Content
%
meansForeach
$_
is for the current pipeline value (this isn’t an alias, but I thought I would define it since you said you were new)
Ok, now here is what is happening in this:
(gc "c:\myfile.txt") |
–> Gets the content ofc:\myfile.txt
and sends it down the line%
–> Does a foreach loop (goes through each item in the pipeline individually)-Begin {$test = (gc "c:\myfile.txt" | select -first 1 -last 1)}
–> This is a begin block, it runs everything here before it goes onto the pipeline stuff. It is loading the first and last line ofc:\myfile.txt
into an array so we can check for first and last items-Process {if ( $_ -eq $test[0] -or $_ -eq $test[-1] )
–> This runs a check on each item in the pipeline, checking if it’s the first or the last item in the file{ $_ -replace "-" } else { $_ }
–> if it’s the first or last, it does the replacement, if it’s not, it just leaves it alone| Set-Content "c:\myfile.txt"
–> This puts the new values back into the file.
Please see the following sites for more information on each of these items:
Get-Content uses
Get-Content definition
Foreach
The Pipeline
Begin and Process part of the Foreach (this are usually for custom function, but they work in the foreach loop as well)
If … else statements
Set-Content
So I was thinking about what if you wanted to do this to many files, or wanted to do this often. I decided to make a function that does what you are asking. Here is the function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
function Replace-FirstLast { [CmdletBinding()] param( [Parameter( ` Position=0, ` Mandatory=$true)] [String]$File, [Parameter( ` Position=1, ` Mandatory=$true)] [ValidateNotNull()] [regex]$Regex, [Parameter( ` position=2, ` Mandatory=$false)] [string]$ReplaceWith="" ) Begin { $lines = Get-Content $File } #end begin Process { foreach ($line in $lines) { if ( $line -eq $lines[0] ) { $lines[0] = $line -replace $Regex,$ReplaceWith } #end if if ( $line -eq $lines[-1] ) { $lines[-1] = $line -replace $Regex,$ReplaceWith } } #end foreach }#End process end { $lines | Set-Content $File }#end end } #end function |
This will create a command called Replace-FirstLast
. It would be called like this:
1 2 |
Replace-FirstLast -File "C:\myfiles.txt" -Regex "-" -ReplaceWith "NewText" |
The -Replacewith
is optional, if it is blank it will just remove (default value of ""
). The -Regex
is looking for a regular expression to match your command. For information on placing this into your profile check this article
Please note: If you file is very large (several GBs), this isn’t the best solution. This would cause the whole file to live in memory, which could potentially cause other issues.