will
will

Reputation: 1

Sorting a file by value using cmd

Good day guys, i'm kind of confused with the SORT command in cmd im trying to sort a text file which is speed.txt which contains these values

>  time=1ms  
time=3ms   
 time=267ms  
 time=4ms    
 time=167ms

I used the command sort /r speed.txt > sorted.txtfor it to output sorted.txt which i expect would contain

>  time=267ms  
time=167ms   
 time=4ms  
 time=3ms    
 time=1ms

but instead the output was

>  time=4ms  
time=3ms   
 time=267ms  
 time=167ms    
 time=1ms

can anyone help me obtain my desired output ? i'm new to cmd commands i'm still at the basic ones

Upvotes: 0

Views: 340

Answers (2)

aschipfl
aschipfl

Reputation: 34909

Here is an approach that can handle numeric values with up to eight digits and even duplicate ones; it relies on the sorting capabilities of the set command:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=%~1"

rem /* Store all lines into an array-style variable set `ARRAY[]` with
rem    the left-zero-padded time value as the first index, the current
rem    line number as the second index to avoid loss of duplicate values
rem    and the current line string are the value: */
for /F "tokens=1* delims== eol==" %%K in ('findstr /N "^" "%_FILE%"') do (
    set "KEY=%%K" & set "VAL=%%L"
    setlocal EnableDelayedExpansion
    set /A "NUM=100000000+VAL, LIN=100000000+KEY"
    set "KEY=!KEY:*:=!"
    for /F "delims=" %%E in ("ARRAY[!NUM:~1!_!LIN:~1!]=!KEY!=!VAL!") do (
        endlocal
        set "%%E"
    )
)

rem // Return values of the array elements sorted by their index:
for /F "tokens=1* delims==" %%K in ('2^> nul set ARRAY[') do (
    echo(%%L
)

endlocal
exit /B

Here is an alternative approach using the sort command and a pipe |:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "_FILE=%~1"

rem /* Precede every line with the sum of `900000000` and the time value,
rem    so the resulting number always consists of nine digits, given that
rem    the value is less than `100000000`; the prefix is separated from
rem    the original text file string by a `|` character; the resulting
rem    text is fed into another block of code via a pipe `|`; since pipes
rem    instantiate new `cmd` instances for either side, and each side is
rem    executed under `cmd` context, the `set /A` command, in contrast to
rem    batch file context, returns the (last) result without a trailing
rem    line-break, so the text of the next `echo` command is appended;
rem    the augmented lines are fed into the `sort` command via a pipe `|`,
rem    the prefix is split off using the `|` character (which the original
rem    line text must not begin with); `sort` does pure string sorting,
rem    but since the number of digits of the prefix is always the same,
rem    the alphabetic (string) order equals the numeric order; finally;
rem    the prefix is removed from the rest, which is the original text: */
(
    rem/ // Read the text file line by line, disregard empty lines:
    for /F "usebackq tokens=1* delims== eol==" %%K in ("%_FILE%") do @(
        rem/ // Extract the numeric part, add "900000000", return result:
        set "VAL=%%L" & set /A "900000000+VAL"
        rem/ // Append "|", followed by the original line string:
        echo(^^^|%%K=%%L
    )
) | (
    rem/ /* Feed augmented text into "sort", read output line by line,
    rem/    split off "|" and everything in front: */
    for /F "tokens=1* delims=|" %%K in ('sort') do @(
        rem/ // Return original line string:
        echo(%%L
    )
)

endlocal
exit /B

Upvotes: 0

user6811411
user6811411

Reputation:

@Echo off&SetLocal EnableExtensions EnableDelayedExpansion
for /f "tokens=3 delims=m=^>" %%A in (speed.txt
) do Set /A "time=10000+%%A"&set "T[!time!]=x"
For /f "tokens=2delims=[]" %%A in ('Set T['
) Do Set /A "time=%%A-10000"&Echo time=!time!ms

Sample output with your exact above input data (assuming a file speed.txt):

time=1ms
time=3ms
time=4ms
time=167ms
time=267ms

The batch parses the input splitting the line at the letter m ( to get rid of the ms) at the equal sign and also at the >. Succesive tokens are counted as one and so it is token 3 to choose. To sort numbers (literally) they have to have the same length, what I accomplish by adding 10000 to each and store them in an array.
The second for /f recalls the array, subtracts the previously added offset 10000 and outputs the resulting number in the format time=#ms

EDIT changed file to speed.txt

Upvotes: 1

Related Questions