I referenced this post. Don't have enough rep to ask on that thread. Find and Replace Using that answer, here is my code
Set /p kmlname=[Type to name the Kml file]
type nul > %kmlname%.kml
type .\kml-start.fmt > %kmlname%.kml
echo off
setlocal enableextensions disabledelayedexpansion
set "search=filename"
set "replace=%kmlname%"
set "textFile=%kmlname%.kml"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
>>"%textFile%" echo(!line:%search%=%replace%!
endlocal
)
I had it working with no errors for some time, but now it is giving an Access is denied, at the For-Do line.
Command Line output example
C:\Users\User\Desktop\Test>setlocal enableextensions disabledelayedexpansion
C:\Users\User\Desktop\Test>set "search=filename"
C:\Users\User\Desktop\Test>set "replace=Test"
C:\Users\User\Desktop\Test>set "textFile=Test.kml"
C:\Users\User\Desktop\Test>for /F "delims=" %%i in ('type "Test.kml" ^& break ^> "Test.kml" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
echo(!line:filename=Test! >>"Test.kml"
endlocal
)
Access is denied.
It does proceed to do the string replacement, but it appears to append each line rather than overwriting each. (It leaves the original content). The kml-start.fmt file is first half of the xml code below.
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:kml="http://www.opengis.net/kml/2.2"
xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>filename</name>
<open>1</open>
<Style id="Photo">
<geomScale>.75</geomScale>
<IconStyle>
<color>ffffffff</color>
<Icon>
<href>root://icons/palette-4.png</href>
<x>192</x>
<y>96</y>
<w>32</w>
<h>32</h>
</Icon>
</IconStyle>
</Style>
<Style id="View">
<IconStyle>
<color>ffffffff</color>
<Icon>
<href>root://icons/palette-3.png</href>
<x>160</x>
<y>128</y>
<w>32</w>
<h>32</h>
</Icon>
</IconStyle>
</Style>
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2"
xmlns:gx="http://www.google.com/kml/ext/2.2"
xmlns:kml="http://www.opengis.net/kml/2.2"
xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>Test</name>
<open>1</open>
<Style id="Photo">
<geomScale>.75</geomScale>
<IconStyle>
<color>ffffffff</color>
<Icon>
<href>root://icons/palette-4.png</href>
<x>192</x>
<y>96</y>
<w>32</w>
<h>32</h>
</Icon>
</IconStyle>
</Style>
<Style id="View">
<IconStyle>
<color>ffffffff</color>
<Icon>
<href>root://icons/palette-3.png</href>
<x>160</x>
<y>128</y>
<w>32</w>
<h>32</h>
</Icon>
</IconStyle>
</Style>
I had not changed the code for this error to occur.
The Windows Command Processor cmd.exe
processing a batch file is the only script interpreter installed by default on Windows which does not support searching in a text file for a string and replacing it with a different string. PowerShell with a PowerShell script or the Windows Script Host with a VBScript or a JScript script would be much better for such a task.
See also: How can you find and replace text in a file using the Windows command-line environment?
The usage of PowerShell would be best for this task as it is the most powerful script interpreter installed by default on Windows since Windows Vista with true support for XML parsing and with support for regular expression replaces.
There could be used also the Batch/JScript hybrid file jrepl.bat with the command line:
jrepl.bat "<name>[^<]*</name>" "<name>Test</name>" /F Test.kml /O -
If that command line is used in a batch file and jrepl.bat
is in the same directory as the batch file, the command line should be in the batch file:
call "%~dp0jrepl.bat" "<name>[^<]*</name>" "<name>Test</name>" /F Test.kml /O -
The file name Test.kml
could be also "%~dp0Test.kml"
if the KML file is in same directory as the batch file which would be in this case better than just Test.kml
as the current directory can be also different to the batch file directory on execution of the batch file.
The lines with the string replacement should be written first into a new file. If the replacement was done successfully, the new file replaces the original file.
Here is a batch file solution for the task using only Windows commands and no other script interpreter.
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "KmlFile=%~dp0Test.kml"
set "Search=<name>filename</name>"
set "Replace=<name>Test</name>"
if not exist "%KmlFile%" exit /B
%SystemRoot%\System32\findstr.exe /I /L /C:"%Replace%" "%KmlFile%" 1>nul && exit /B
(for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /N "^" "%KmlFile%"') do (
set "Line=%%I"
setlocal EnableDelayedExpansion
set "Line=!Line:%Search%=%Replace%!"
echo(!Line:*:=!
endlocal
)) 1>"%KmlFile%.tmp"
if not exist "%KmlFile%.tmp" exit /B
%SystemRoot%\System32\fc.exe /B "%KmlFile%" "%KmlFile%.tmp" 1>nul && goto DelTempFile
for %%I in ("%KmlFile%") do set "PropertyAttributes=%%~aI"
if "%PropertyAttributes:r=%" == "%PropertyAttributes%" (move /Y "%KmlFile%.tmp" "%KmlFile%" 1>nul) else echo File "%KmlFile%" is read-only!
:DelTempFile
del "%KmlFile%.tmp" 2>nul
endlocal
NOTE: This batch file does not work for a UTF-16 encoded XML file.
There is defined with the first two lines the required execution environment with:
Please modify the third line set "KmlFile=%~dp0Test.kml"
and use the correct file name. This command line defines the file name of the KML file which is expected in the directory of the batch file referenced with %~dp0
which always expands to a directory path ending with a backslash.
Then is checked if the KML file exists at all. An immediate exit of the batch file processing is done as nothing else to do in case of the KML file does not exist where it is expected.
There is used next FINDSTR to check if the line with the element name
already has the right file name as value in which case no modification is necessary, and the processing of the batch file is exited too.
If name
value is not the right file name, there is a loop executed processing all lines of the KML file including empty lines usually skipped by FOR /F to create a copy of the KML file with file extension .tmp
in which the line containing <name>filename</name>
is written into the temporary file with <name>Test</name>
. See my answer on How to read and print contents of text file line by line? for a description of most parts of the FOR /F loop.
The creation of the temporary file could fail for some reason like a write-protected directory for the current user. In this case the processing of the batch file is exited without modifying the KML file as not possible.
A binary file comparison is done to determine if the temporary file is byte by byte identical to the KML file. The KML file is kept as is if the KML file is identical with the temporary file like on KML file not containing case insensitive <name>filename</name>
at all.
The file attributes of the KML file are determined next. If a user set the read-only attribute on the KML file to prevent its modification, this user configuration is taken into consideration and the KML file is not updated in this case.
The KML file is replaced with the temporary file with the modified line with <name>Test</name>
on not being read-only. The last modification date of the KML file is changed only if the file content is really changed by the batch file.
The temporary file should not exist anymore on typical use cases. The still existing temporary file is deleted by the batch file if the KML file does not contain a line with <name>filename</name>
, or it is read-only, or it is opened in another application which prevents replacing the KML file with the temporary file.
The initial environment is restored with the last command line as it is also done implicit by the Windows Command Processor on exiting the processing of this batch file. The last line could be removed for that reason.
The task description in the question changed from making a string replace in an already existing KML file to copying a template file to a new KML file with modification of a string during creation of the copy.
The usage of jrepl.bat
in the batch file would require now the command line:
call "%~dp0jrepl.bat" "<name>filename</name>" "<name>%kmlname%</name>" /L /F "%~dp0kml-start.fmt" /O "%kmlname%.kml"
An entire batch file not using any other script interpreter could be for this task:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "TemplateFile=%~dp0kml-start.fmt"
if not exist "%TemplateFile%" exit /B
:NamePrompt
set "KmlFile="
set /P "KmlFile=Type to name the Kml file: " || goto NamePrompt
set "KmlFile=%KmlFile:"=%"
if not defined KmlFile goto NamePrompt
for %%I in ("%KmlFile%") do set "Replace=<name>%%~nI</name>" & if /I not "%%~xI" == ".kml" set "KmlFile=%KmlFile%.kml"
set "Replace=%Replace:&=&%"
set "Search=<name>filename</name>"
(for /F delims^=^ eol^= %%I in ('%SystemRoot%\System32\findstr.exe /N "^" "%TemplateFile%"') do (
set "Line=%%I"
setlocal EnableDelayedExpansion
set "Line=!Line:%Search%=%Replace%!"
echo(!Line:*:=!
endlocal
)) 1>"%KmlFile%"
endlocal
The user is prompted for the file name of the KML file to produce. There is checked by the code if the user enters something at all. The prompt is shown once again on user hits by mistake RETURN or ENTER without entering a string. All "
are removed on user is entering a string with one or more double quote characters. If the file name string becomes an empty string on removal of all double quotes, the user is prompted once again. For more details about these checks see: How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?
The user has the freedom to enter just the file name without file extension .kml
or with that file extension in any case. The user can enter the file name also with an absolute or relative path.
The batch file does not check if the user input string is valid for a file name. The redirection of the output lines in last but one line results in the following error message on file name entered by the user is not valid:
The filename, directory name, or volume label syntax is incorrect.
If a KML file with the user input file name already exists and is write-protected by a read-only attribute, by appropriate file permissions or because of being opened currently in an application with shared write access denied as long as being opened in the application, there is displayed the error message:
Access is denied.
Every &
in user input file name is replaced by &
in the replace string to produce a valid XML file also in this special use case.
The batch file does not convert non-ASCII characters in file name to the correct UTF-8 byte sequences for such characters. There is no Windows command supporting a string conversion from the active OEM code page to UTF-8 encoding. Users of the batch file should enter for that reason only a file name with ASCII characters valid for a file name.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
call /?
… explains %~dp0
… full batch file directory pathdel /?
echo /?
endlocal /?
exit /?
fc /?
findstr /?
for /?
goto /?
if /?
move /?
set /?
setlocal /?
See also single line with multiple commands using Windows batch file for an explanation of the unconditional command operator &
as used in one FOR command line and the conditional command operator ||
on set /P
command line for a more efficient batch file processing.