从文本文件中回显指定的行

Echo specified lines from a text file

这可能是一个 XY 问题,但我正在尝试回显文本文件中的各行。

@setlocal enableextensions enabledelayedexpansion
@echo off
set /a "line = 0"
for /f "tokens=* delims= " %%a in (file.txt) do (
    set /a "line = line+1"
    if !line!==18 set thing1=%%a
    if !line!==19 set thing2=%%a
    if !line!==20 set thing3=%%a
)
endlocal & set thing1=%thing1% & set thing2=%thing2% & set thing3=%thing3%
echo:
echo %thing1%
echo %thing2%
echo %thing3%
pause

与我发现的其他人相比,它工作得很好并且很整洁,所以我很累让它更具适应性。我可以使行号可变,但如果我想要四行或一系列行怎么办?所以我试着做一个for循环。 列出所有行:

(
    echo @setlocal enableextensions enabledelayedexpansion
    echo @echo off
    echo set /a "line = 0"
    echo for /f "tokens=* delims= " %%a in (file.txt^) do (
        echo     set /a "line = line+1"
    )>>test.bat
    for /l %%m in (1,1,10) do (
        echo     if !line!==%%m set thing%%m=%%%%a
    )>>test.bat
    echo )>>test.bat
echo      endlocal ^& )>>test.bat
for /l %%m in (1,1,10) do (
    echo     set thing%%m^=%%thing%%%%m ^&  
)>>test.bat

那我就可以echo %thing(anynumber%)如我所愿了。 当 for 循环列表需要在同一行时,这会遇到问题:

endlocal & set thing1=%thing1% & set thing2=%thing2% & set thing3=%thing3%

而是输出:

endlocal & 
    set thing1=%thing%1 & 
    set thing2=%thing%2 & 
    set thing3=%thing%3 & 
    etc...

我知道 prompt $H 可以退格,但我认为这是我在这里尝试的死胡同。我在网上找不到太多关于反向换行的信息。它还向列表中的最后一个 %%thing%% 添加了一个符号。

样本file.txt

This is line one
This is line two
This is line three
This is line four
This is line five
This is line six
This is line seven
This is line eight
This is line nine
This is line ten

也许 findstr 是解决这个问题的方法。我找到了这个并对其进行了编辑以适合我想要做的事情:

:start
cls
set /p "line= Which lines?" 
for /f "tokens=*  delims=[] " %%a in ('type file.txt^|find /v /n ""') do (
    echo/%%a|findstr /l /b "%line%" >nul && echo/%%a
)
pause
goto :start

但是当输入 1 时,它会回显以“1”开头的每个实例,即 1、11、12、13 等。这似乎差不多了。我尝试了 findstr /? 的各种开关,但无法弄清楚。如果它也可以输入范围,那么输入行 1-5、7、12-15 将回显它们 10 行。

应该这样做(虽然不是范围):

@echo off
setlocal 

set lines=1,5,6,8
(for %%a in (%lines%) do echo %%a:)>lines.txt

for /f "tokens=1,* delims=:" %%a in ('type file.txt^|findstr /n "^"^|findstr /bg:lines.txt') do echo/%%b
del lines.txt

第一个 for 循环为 findstr /g 构建一个临时文件(有关详细信息,请参阅 findstr /?)。
第二个添加行号,在文件中查找它们,如果行号在文件中,则打印原始行。

%lines% 中的数字可以用任何标准分隔符分隔(制表符、空格、逗号甚至 =(不推荐 - 保留空格 and/or 逗号)

要扩展到范围 5-10,您需要用更多代码解析 %lines%(很难做到万无一失),并将 "translate" 解析为单行数字写入 lines.txt

(我们也可以将 %lines% 扩展为 REGEX 搜索字符串或 findstr,避免使用临时文件,但这样更容易理解和维护)

编辑: 实现了一个简单的 "range extension"(没有检查合理性):

@echo off 
setlocal 

set "lines=1,4-6,9"

(for %%a in (%lines%) do (
  echo %%a|find "-" >nul && call :range %%a || echo/%%a:
))>lines.txt

echo lines %lines% are:
for /f "tokens=1,* delims=:" %%a in ('type file.txt^|findstr /n "^"^|findstr /bg:lines.txt') do echo/%%b
del lines.txt
goto :eof

:range
for /f "tokens=1,2 delims=-" %%b in ("%1") do (
  for /l %%i in (%%b,1,%%c) do echo %%i:
)
goto :eof

输出(使用您的示例输入文件):

lines 1,4-6,9 are:
This is line one
This is line four
This is line five
This is line six
This is line nine

PS:这会按原始顺序输出行(set "lines=1,4-6,8"set "lines=8 1 4-6" 给出相同的输出(由于 findstr /g 的工作方式))

我不是很了解数组。下周我再看看。到目前为止,我已经根据@Stephan 的回答得到了这个。

choice /c rs /m "RANGE MODE OR SPECIFIED MODE"
goto:%errorlevel%
:2
:specified
echo:
set /p lines=ENTER SPECIFIC LINES (seperated by spaces)? 
(for %%a in (%lines%) do echo %%a:)>lines.txt
for /f "tokens=1,* delims=:" %%a in (
    'type file.txt^|findstr /n "^"^|findstr /bg:lines.txt'
    ) do (
    echo/%%b)
del lines.txt
pause
goto :eof

:1
:range
echo:
set /p ran=ENTER RANGE (e.g. 15-25)? 
echo %ran%>range.txt
for /f "tokens=1,* delims=-" %%a in (range.txt) do (
    set line1=%%a
    set line2=%%b)
del range.txt
(for /l %%a in (%line1%,1,%line2%) do echo %%a:)>lines.txt
for /f "tokens=1,* delims=:" %%a in (
    'type file.txt^|findstr /n "^"^|findstr /bg:lines.txt'
    ) do (
    echo/%%b)
del lines.txt
pause
goto :eof

尝试通过 this link 跟踪数组 我可以看到在批处理中设置元素但没有指向 file.txt。我试过了

for /f %%a in ('type file.txt^|echo !elem[%2%]!') do echo %%a

      for /f "tokens=*  delims=" %%a in ('type file.txt^|find /v /n ""') do (
    echo/%%a|findstr /l /b "!elem[%2%]!" >nul && echo echo/%%a)

无论如何,这是一个不同的问题,我下周会解决这个问题 link。

从文件的顶部或底部减少行数。

这类似于 Unix 的 Tail 命令,后者没有 Windows' 等价物。

使用

剪切

cut {t|b} {i|x} NumOfLines

从文件的顶部或底部减少行数。

t - top of the file
b - bottom of the file
i - include n lines
x - exclude n lines

例子

 cscript //nologo c:\folder\cut t i 5 < "%systemroot%\win.ini"

将以下行复制到 cut.vbs

Set Arg = WScript.Arguments
set WshShell = createObject("Wscript.Shell")
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
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 ASC"

    If LCase(Arg(1)) = "t" then
        If LCase(Arg(2)) = "i" then
            .filter = "LineNumber < " & LCase(Arg(3)) + 1
        ElseIf LCase(Arg(2)) = "x" then
            .filter = "LineNumber > " & LCase(Arg(3))
        End If
    ElseIf LCase(Arg(1)) = "b" then
        If LCase(Arg(2)) = "i" then
            .filter = "LineNumber > " & LineCount - LCase(Arg(3))
        ElseIf LCase(Arg(2)) = "x" then
            .filter = "LineNumber < " & LineCount - LCase(Arg(3)) + 1
        End If
    End If

    Do While not .EOF
        Outp.writeline .Fields("Txt").Value

        .MoveNext
    Loop
End With

对于行计数程序

Set Arg = WScript.Arguments
set WshShell = createObject("Wscript.Shell")
Set Inp = WScript.Stdin
Set Outp = Wscript.Stdout
Do Until Inp.AtEndOfStream
    Line=Inp.readline
    Count = Count +1
Loop
outp.writeline Count