在.bat文件中使用PowerShell,用多个字符串替换字符串

时间:2017-02-06 13:09:00

标签: powershell batch-file replace windows-10

我正在使用.baat将多个文件移动到另一个文件夹中,但在实际移动部分之前,我想要替换LAST行(它是已知的行),例如我有一个文件output.txt就像这样:

HEADER
    BODY
 FOOTER

使用以下代码片段:

powershell -Command "(gc output.txt) -replace 'FOOTER', 'ONE_MORE_LINE `r`n FOOTER' | Out-File output.txt"

我期待的回报是

HEADER
    BODY
 ONE_MORE_LINE
 FOOTER

但我得到的是:

HEADER
    BODY
 ONE_MORE_LINE `r`n FOOTER

我试过了:

  • \n
  • <br>
  • "`r`n"
  • "`n"
  • echo ONE_MORE_LINE >> output.txt; echo. >> output.txt; echo FOOTER >> output.txt"

最后一个接近,但结果是一些破碎的字符。

欢迎除PowerShell之外的其他建议。我只是使用它,因为这是一个简单的方法来添加行并替换它。

编辑: 试过这个命令

powershell -Command "(gc output.txt) -replace 'FOOTER;', ""ONE_MORE_LINE `r`n FOOTER"" | Out-File output.txt "

并返回此错误:

A cadeia de caracteres não tem o terminador: ".
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString 

EDIT2 - 可能的解决方案:

我意识到使用PowerShell命令改变了文件的编码,打破了echo ONE_MORE_LINE,并使用@AnsgarWiechers的建议,我制作了这段代码

findstr /v "FOOTER" output.sql > new_output.sql
TYPE new_output.sql > output.sql
del new_output.sql
ECHO. >> %%f
ECHO ONE_MORE_LINE >> %%f
ECHO FOOTER >> %%f
ECHO. >> %%f

它的作用是使用commant findstr /v“FOOTER”我查找文件output.sql中不是FOOTER的所有行并将其写入new_output.sql

然后我TYPE回到原始文件,DEL new_output.sql

然后我Echo我需要的所有行。

它可以工作但是对于大文件我认为重写两次会花费很多时间,但我无法找到其他解决方案。

1 个答案:

答案 0 :(得分:0)

使用大文件时,最好使用文件流。使用批处理for /f循环或使用PowerShell中的Get-Content逐行读取文件以将整个文件读入内存的更典型方法可以减慢使用大文件进行爬网的过程。另一方面,使用文件流,您几乎可以立即从文件末尾搜索到最后一行的开头,插入所需的数据,然后重新组合您覆盖的字节。

以下示例将使用PowerShell对.NET方法的访问权限将文件作为字节流打开,以便快速读取和写入。有关详细信息,请参阅内联注释希望保留文件编码。使用 .bat 扩展程序保存,并试一试。

<# : batch portion
@echo off & setlocal

set "file=test.txt"
set "line=Line to insert!"

powershell -noprofile "iex (${%~f0} | out-string)"
goto :EOF
: end batch / begin PowerShell hybrid #>

# construct a file stream for reading and writing $env:file
$IOstream = new-object IO.FileStream((gi $env:file).FullName,
    [IO.FileMode]::OpenOrCreate, [IO.FileAccess]::ReadWrite)

# read BOM to determine file encoding
$reader = new-object IO.StreamReader($IOstream)
[void]$reader.Read((new-object byte[] 3), 0, 3)
$encoding = $reader.CurrentEncoding
$reader.DiscardBufferedData()

# convert line-to-insert to file's native encoding
$utf8line = [Text.Encoding]::UTF8.GetBytes("`r`n$env:line")
$line = [Text.Encoding]::Convert([Text.Encoding]::UTF8, $encoding, $utf8line)
$charSize = [math]::ceiling($line.length / $utf8line.length)

# move pointer to the end of the stream
$pos = $IOstream.Seek(0, [IO.SeekOrigin]::End)

# walk back pointer while stream returns no error
while ($char -gt -1) {
    $IOstream.Position = --$pos
    $char = $reader.Peek()
    $reader.DiscardBufferedData()

    # break out of loop when line feed preceding non-whitespace is found
    if ($foundPrintable) { if ($char -eq 10) { break } }
    else { if ([char]$char -match "\S") { $foundPrintable++ } }
}

# step pointer back to carriage return and read to end into $buffer with $line prepended
$pos -= $charSize
$IOstream.Position = $pos
$buffer = $encoding.GetBytes($encoding.GetString($line) + $reader.ReadToEnd())
$IOStream.Position = $pos

"Inserting data at byte $pos"
$IOstream.Write($buffer, 0, $buffer.Length)

# Garbage collection
$reader.Dispose()
$IOstream.Dispose()

此方法应该比从头开始读取文件或将整个文件复制到内存或插入新行的磁盘上更有效。在我的测试中,它在大约1/3秒内将该行插入到一个100兆的文件中。