【问题标题】:Modify a string in a .properties file with batch使用批处理修改 .properties 文件中的字符串
【发布时间】:2018-11-15 12:28:36
【问题描述】:

我正在尝试通过执行脚本来修改我的 csm.properties 中的某个属性。 我查了很多,最后找到了这段代码。

set "search=CLASSPATH"
set "insert=CLASSPATH^=plugins^/Numbering.jar^\^:"

set "textFile="%workingPlace%bin\csm.properties""

FOR /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
    FOR /f "tokens=1*delims==" %%g IN ("%%i") DO (
        IF /i "%%g" == %search% (
            set "line=%%i"
                setlocal enabledelayedexpansion
                >>"%textFile%" echo(!line:%search%=%insert%!
                endlocal
        )ELSE (
        %%i
        )
    )
)

此代码应读取我文件中的每一行并使用= 作为分隔符。如果代码将“CLASSPATH”作为属性,则应修改该行。 但似乎找不到 CLASSPATH。

这就是 csm.properties 的样子:

#Tue Jul 10 08:50:23 CEST 2018
JAVA_ARGS=-Xmx20000M -DLOCALCONFIG\=true -splash\:data/splash.png -Dmd.class.path\=$java.class.path -Dcom.nomagic.osgi.config.dir\=configuration -Desi.system.config\=data/application.conf -Dlogback.configurationFile\=data/logback.xml -Dsun.locale.formatasdefault\=true -Dinitial.user.language\=de
JAVA_HOME=jre1.8.0_152
BOOT_CLASSPATH=lib/xalan.jar
MAIN_CLASS=com.nomagic.osgi.launcher.ProductionFrameworkLauncher
MAC_JAVA_ARGS="-Xdock\:name\=Cameo Systems Modeler" -Xdock\:icon\=bin/md.icns -Dapple.laf.useScreenMenuBar\=true
APP_ARGS=
DEFAULT_MEMORY_SETTINGS_64=-Xmx[30%,1200,4000]M
DEFAULT_MEMORY_SETTINGS_32=-Xmx800M
CLASSPATH=lib/patch.jar\:lib/brand_api.jar
CONSOLE=false

修改后CLASSPATH应该是这样的:

CLASSPATH=plugins/Numbering.jar\:lib/patch.jar\:lib/brand_api.jar

【问题讨论】:

  • 不幸的是,我不允许使用 powershell。因此,将结果流水线化到一个新文件并用最新的文件覆盖旧文件,我希望能完成这项工作吗?
  • 那么,如果我在第一个循环中使用我的 delims 和 tokens,结果会一样吗?
  • 我可以进行批处理,但它需要写入一个新文件,然后删除旧文件并重命名新文件。你想要的结果相同。
  • 我很想看看你的解决方案。
  • 还有关于我的 2 个 for 循环。我想,因为) ELSE ( %%i ) 行,我需要它们。如果我没有这两个循环%%i 只会返回=之前的字符串

标签: batch-file cmd insert


【解决方案1】:

更简单...

@echo OFF
setlocal

set "search=CLASSPATH"
set "insert=plugins/Numbering.jar\:"

set "textFile=%workingPlace%bin\csm.properties"

(FOR /f "usebackq tokens=1* delims==" %%i in ("%textFile%") do (
   if "%%i" equ "%search%" (
      echo %search%=%insert%%%j
   ) else if "%%j" neq "" (
      echo %%i=%%j
   ) else (
      echo %%i
   )
)) > temp.tmp

move /Y temp.tmp "%textFile%"

【讨论】:

  • 这可能会以这种方式工作,但我不能硬编码\:lib/patch.jar\:lib/brand_api.jar,因为类路径因用户而异。我正在为多人编写此脚本
  • 刚刚解决了这一点...所以,您真的不是“替换”,而是“插入”一个新值,不是吗?
  • 没错。我现在正在执行您的代码,但执行后整个文件为空。所以我猜还是有问题
  • 去掉move命令和> temp.tmp部分,确认输出正确...
  • ...就像我在set "textFile=%workingPlace%bin\csm.properties" 命令中所做的...
【解决方案2】:

你可以试一试:

@echo off
setlocal enableextensions disabledelayedexpansion

 set "replace=plugins^/Numbering.jar^\^:"
 set "textFile=%workingPlace%bin\csm.properties""

  for /f %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
     set "line=%%i"
     for /f "tokens=1* delims==" %%a in ("%%i") do (
     if "%%a"=="CLASSPATH" (
        setlocal enabledelayedexpansion
        >>"%textFile%" echo(!line!%replace%
        ) else (
        setlocal enabledelayedexpansion
        >>"%textFile%" echo(!line!
        endlocal
     )
  )
)

类似的理论,只有第一个token(%%i)匹配CLASSMAP时才会替换整个字符串

请不要更改set 命令中的双引号。

【讨论】:

  • 不幸的是,我得到一个语法错误作为输出
  • 什么被声明为%%j
  • 请复制并重试
  • %%j 是每行 = 之后的值
  • 嗯.. 它有点工作,但现在 CLASSPATH 中的其他所有内容都已删除。我只是想在 CLASSPATH 中添加一个新字符串
【解决方案3】:

这是我仅使用 cmd.exe 的内部命令完成此字符串替换任务的解决方案,FINDSTR 除外。

@echo off
setlocal EnableExtensions DisableDelayedExpansion

if not defined workingPlace set "workingPlace=%~dp0"
set "TextFile=%workingPlace%bin\csm.properties"
if not exist "%TextFile%" goto EndBatch

set "TempFile=%TEMP%\csm.properties.tmp"
set "FoundInfo="

(for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /N "^" "%TextFile%"') do (
    set "Line=%%I"
    setlocal EnableDelayedExpansion
    set "Line=!Line:*:=!"
    if not defined FoundInfo (
        if defined Line (
            if /I "!Line:~0,10!" == "CLASSPATH=" (
                if /I "!Line!" == "CLASSPATH=" (
                    echo !Line!plugins/Numbering.jar
                    endlocal
                    set "FoundInfo=1"
                ) else if "!Line:plugins/Numbering.jar=!" == "!Line!" (
                    set "Line=!Line:~0,10!plugins/Numbering.jar\:!Line:~10!"
                    echo !Line!
                    endlocal
                    set "FoundInfo=1"
                ) else (
                    endlocal
                    goto DeleteTempFile
                )
            ) else (
                echo(!Line!
                endlocal
            )
        ) else (
            echo/
            endlocal
        )
    ) else (
        echo(!Line!
        endlocal
    )
))>"%TempFile%"

if not defined FoundInfo echo CLASSPATH=plugins/Numbering.jar>>"%TempFile%"
move /Y "%TempFile%" "%TextFile%"

:DeleteTempFile
if exist "%TempFile%" del "%TempFile%"

:EndBatch
endlocal

阅读我在 How to read and print contents of text file line by line? 上的回答,为什么命令 FINDSTR 仅用于输出文件 csm.properties 中的每一行,包括默认情况下被 FOR 忽略的空行以及行号和: 以避免任何行被FOR 忽略。命令行set "Line=!Line:*:=!"去掉行号和冒号。

在批处理文件的顶部有一个环境变量FoundInfo undefined,一旦FOR循环的内部代码处理了以CLASSSPATH=开头的不区分大小写的行,就会设置该环境变量。文件中以CLASSSPATH= 开头的行之后的每一行都只是输出,无需进一步处理,包括空行。

CLASSSPATH= 开头的行上方的空行也以echo/ 输出,无需任何进一步处理。

CLASSPATH= 开头的第一行不区分大小写,可以用三种不同的方式处理:

  1. 该行仅包含 CLASSPATH=
    在这种情况下,该行输出为CLASSPATH=plugins/Numbering.jar,就是这样。
  2. 该行以CLASSPATH= 开头,包含一个或多个字符,但不区分大小写的字符串plugins/Numbering.jar。 在这种情况下,该行在CLASSPATH= 之后插入plugins/Numbering.jar\: 内输出。
    请注意,仅包含 CLASSPATH= 和一个或多个尾随空格/制表符的行也会导致运行到第二个分支,例如输出 CLASSPATH=plugins/Numbering.jar\: \: 以及末尾的尾随空格。
  3. 该行以CLASSPATH= 开头,并且该行某处包含不区分大小写的字符串plugins/Numbering.jar
    在这种情况下,FOR 循环立即退出,并跳转到标签DeleteTempFile,而不处理来自 FINDSTR 捕获输出的任何其他行。所以文件的最后修改日期不会改变,因为文件内容没有改变。 (我不喜欢更改未真正修改的文件内容的最后修改日期。)

FOR 循环之后检查文件中是否有任何以CLASSPATH= 开头的不区分大小写的行。如果不是这种情况,则CLASSPATH=plugins/Numbering.jar 行将附加到临时文件中。

最后,临时文件肯定与csm.properties 不同,如果可能的话,临时文件将移动到现有文件csm.properties 上,最后如果临时文件仍然存在,则将其删除。

注意 1: 如果文件 csm.properties 不包含空行或在更新期间删除空行是可以接受的,则不使用 FINDSTR 解决方案可能会更容易与CLASSPATH=一致。

注意 2:CLASSPATH= 位于文件 csm.properties 顶部的行减少了处理时间。

本方案的特点总结:

  1. 不修改已包含CLASSPATH=plugins/Numbering.jar 的文本文件。
  2. 仅当此行上有其他类路径(或尾随空格)时,才在 CLASSPATH= 之后插入 plugins/Numbering.jar\:
  3. plugins/Numbering.jar 附加到现有的 CLASSPATH= 行,不包含任何其他类路径(并且此行中没有尾随空格)。
  4. 如果文件至少存在,则将整个CLASSPATH= 行和plugins/Numbering.jar 附加到根本不包含该行的文件中。
  5. 在文本文件中保留空行,因此只修改开头带有CLASSPATH= 的行。
  6. 不会将带有VARIABLE==value(开头带等号的值)的行修改为VARIABLE=value(删除开头的等号)。
  7. 不修改CLASSPATH= 的拼写,因此也适用于文件中的classpath=ClassPath=
  8. 不删除以 ; 开头的行,这是 FOR 的行尾选项 (eol) 的默认值。

要了解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

  • del /?
  • echo /?
  • endlocal /?
  • findstr /?
  • for /?
  • goto /?
  • if /?
  • move /?
  • set /?
  • setlocal /?

另请参阅Why is no string output with 'echo %var%' after using 'set var = text' on command line?How to set environment variables with spaces? 这些答案解释了为什么在大多数情况下set variable="value" 不好以及与set "variable=value" 有什么区别,set "variable=value" 是定义具有字符串值的环境变量的首选语法。

另请参阅 Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files 解释字符串比较如何与命令 IF 一起工作,以及为什么运算符 EQUNEQ 设计为主要用于整数比较通常不应该用于比较两个字符串,尽管这个有可能。在某些情况下,使用EQUNEQ 进行字符串比较可能会导致意外的比较结果。

【讨论】:

    【解决方案4】:
    @echo off
    setlocal disabledelayedexpansion
    
    set "search=CLASSPATH"
    set "insert=plugins/Numbering.jar\:"
    
    set "textFile=%workingPlace%bin\csm.properties"
    
    for /f "usebackq delims=" %%i in ("%textFile%") do (
        for /f "tokens=1* delims==" %%g in ("%%i") do (
            if /i "%%g" == "%search%" (
                set "token1=%%g"
                set "token2=%%h"
                setlocal enabledelayedexpansion
                >> "%textFile%.tmp" echo(!search!=!insert!!token2!
                endlocal
            ) else (
                set "line=%%i"
                setlocal enabledelayedexpansion
                >> "%textFile%.tmp" echo(!line!
                endlocal
            )
        )
    )
    
    move "%textFile%" "%textFile%.bak" && move "%textFile%.tmp" "%textFile%"
    

    似乎需要插入而不是替换。

    for循环读取%textfile%的每一行和嵌套的for循环 在= 上定界以存储令牌 1 和令牌 2 以及余数。

    如果找到%search%,那么!token1!!token2!就是set 令牌值。 set 之后通常可以处理毒字 所以为什么要使用它。扩展被延迟所以毒字符 被回显到文件而不被扩展到源中。

    如果没有找到%search%,则该行设置为!line!, 扩展延迟,然后该行回显到文件。

    注意: %workingPlace% 未知,因此请根据需要更正路径。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多