如何使用批处理从底部向上读取文本文件

3

我正在处理一个需要创建特定资源的项目,当创建资源时,我还会在文件中写入内容以便在出现错误时回滚该创建过程。但是这里有一个注意点,这些资源必须按与它们创建方式相反的方式进行删除,因此当我在回滚文件中编写如下内容时:

delete R1
delete R2
..
..
delete RN

我想按顺序删除资源 ->
delete RN
delete RN-1
....
...
delete R1

有没有一种方法可以在Windows批处理中实现这个功能?
3个回答

3

批处理中没有本地读取文件的方法来倒序读取。

但是,虽然不是最快的方法,可以通过按正常顺序读取文件,在每行前缀中添加填充数字,将列表按反向排序,拆分行以分隔数字,然后处理列表来解决这个问题。

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "file=data.txt"

    for /f "tokens=1,* delims=¬" %%a in ('
        cmd /v:off /e /q /c"set "counter^=10000000" & for /f usebackq^ delims^=^ eol^= %%c in ("%file%") do (set /a "counter+^=1" & echo%%c)"
        ^| sort /r
    ') do (
        echo %%b
    )

工作原理:

对于输入文件中的每一行,该代码将输出一个数字前缀和该行,使用分隔符(¬)以便稍后能够分离这两个元素。

由于稍后数据将被排序,我们需要前缀具有统一的长度。因此,我们将计数器初始化为 10000000,并递增,使所有行的前缀具有相同的长度(注意:如果您的文件有超过99999999行,请不要使用此方法)

要更改计数器,我们只需使用 set /a "counter+=1",但为了在循环内部回显它,我们需要延迟扩展,但这可能会对从文件中读取的数据产生问题(我不知道它是否可以包含 ! 字符)。

我们可以启用/禁用延迟扩展来处理它,但由于这对于每一行都是必要的,因此将导致性能损失。为了解决这个问题,代码在单独的 cmd 实例中执行。

为什么要单独的实例?这个单独的实例中的代码不是在批处理上下文中执行的,而是在命令行上下文中执行的。在命令行上下文中,set /a 命令会回显计算结果,因此我们不需要这样做,并且延迟扩展问题消失了。我们只需计算新的计数器(它被回显),并回显从输入文件中读取的分隔符和行。因此,我们有以下代码:

set "counter=10000000" 
for /f usebackq^ delims^=^ eol^= %%c in ("%file%") do (
    set /a "counter+=1" 
    echo%%c
)

这是一个连接命令,并作为参数传递给 cmd 实例(注意:使用^来转义问题字符),然后使用echo off/q)、启用扩展(/e)和禁用延迟扩展(/v:off)执行。

cmd /v:off /e /q /c"set "counter^=10000000" & for /f usebackq^ delims^=^ eol^= %%c in ("%file%") do (set /a "counter+^=1" & echo%%c)"

这将生成带有前缀的输出。现在,要按相反顺序排序,需要将输出管道传递到 sort /r 命令。

cmd /c"..." | sort /r

由于管道需要进行处理(在本示例中我们仅将该行echo到控制台上),完整的管道在for /f %%a中执行(有更多的^转义字符)。

 for /f "tokens=1,* delims=¬" %%a in ('
      cmd /c"..." ^| sort /r
 ') do ....

这个for命令表示使用¬字符作为分隔符来分割输入行(管道执行的结果),我们想要获取第一个标记(数字前缀,虽然不会用到但需要包含)存储在%%a中,以及行的其余部分(没有额外的拆分)存储在%%b中。

因此,在for /f %%ado子句中的代码将按照反向顺序对输入文件中的每一行进行执行,并将该行存储在%%b中。


delims=¬" %%a 中的 ¬ 有什么用途?此外,您能否更详细地解释一下您的解决方案。 - Subham Tripathi
1
@SubhamTripathi,回答已更新。请检查是否清晰明了。 - MC ND
你按哪个键可以打印出 ¬,因为我在我的键盘上找不到它 :) - Subham Tripathi
@SubhamTripathi,对我来说是AltGr+6(在西班牙键盘上),但您可以复制/粘贴它或使用几乎任何其他字符。选择一个不包含在您的数据中或至少与每行起始字符不匹配的字符。只需更改包含分隔符的“echo”和使用它的“delims”子句即可。 - MC ND
对我来说alt + 6无效,不过我理解你的意思。 - Subham Tripathi

0

这个程序可以将文件中的文本行反转。

交换

filter swap

将文件中的行反转,使顶部行变为底部行。

示例

cscript //nologo filter.vbs swap <"%systemroot%\win.ini"

从Filter.vbs来自https://skydrive.live.com/redir?resid=E2F0CE17A268A4FA!121 - 这里有19个用于处理文件中行的vbs程序示例。它还包括一个批处理文件,使其易于使用。

Set Arg = WScript.Arguments
set WshShell = createObject("Wscript.Shell")
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
    Dim LineCount
    Set rs = CreateObject("ADODB.Recordset")
    With rs
        .Fields.Append "LineNumber", 4 
        .Fields.Append "Txt", 201, 5000 
        .Open
        LineCount = 0
        Do Until Inp.AtEndOfStream
            LineCount = LineCount + 1
            .AddNew
            .Fields("LineNumber").value = LineCount
            .Fields("Txt").value = Inp.readline
            .UpDate
        Loop
        .Sort = "LineNumber DESC"
        Do While not .EOF
            Outp.writeline .Fields("Txt").Value
            .MoveNext
        Loop
    End With

抱歉,但我需要使用普通批处理解决方案,感谢您的帮助 :) - Subham Tripathi

0
你可以使用这个方法:
Del /F /Q "%Temp%\File.Sort.Reverse.txt" >Nul: 2>&1
SetLocal EnableDelayedExpansion
For /F %%a In ('Type "FileName" ^| Find /C /V ""') Do Set /A File.Lines.Count=%%a
Set /A File.Lines.Count+=1
For /F %%a In ('Type "FileName') Do Set /A File.Lines.Count-=1 & Set "File.Line.Number=00000000000000000000!File.Lines.Count!" & @Echo.[!File.Line.Number:~-20!]%%a>>"%Temp%\File.Sort.Reverse.txt"
EndLocal
For /F "Tokens=1* Delims=[]" %%a In ('Type "%Temp%\File.Sort.Reverse.txt" ^| Sort') Do Echo.%%b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接