How can i change the xml file values by bat commands. 'sed' is not recognized as internal or external command - batch-file

Folks,
Trying to update the XML file values using 'sed'. bat command not recognized.
CODE:
**#echo off
SET PREFIX=ROOM107-
SET SUFFIX=\Admin
SET /P PCNAME1=Please enter your desired NUMBER:
SET PCNAME=%PREFIX%%PCNAME1%%SUFFIX%
sed -i "s#<UserId>Admin</UserId>#<UserId>%PCNAME%</UserId>#" changexml.xml
pause**
Any other way to update the below XML file using BAT command ?
XML:
<xml>
<Main_group>
<group_1>
<add key="A" value="AMERICA"/>
<add key="B" value="BALL"/>
</group_1>
<group_2>
<add key="A" value="AMERICA"/>
<add key="B" value="BALL"/>
</group_2>
</Main_group>
<SubGroup>
<add key="A" value="AMERICA"/>
<add key="B" value="BALL"/>
</SubGroup>
</xml>
trying to update the value of 'A' and 'B' of the node
like below
<SubGroup>
<add key="A" value="USA"/>
<add key="B" value="Basket Ball"/>
</SubGroup>

Your xml was not valid so I've edited it a little bit (saved as xml.txt):
<xml>
<Main_group>
<group_1>
<add key="A" value="AMERICA"/>
<add key="B" value="BALL"/>
</group_1>
<group_2>
<add key="A" value="AMERICA"/>
<add key="B" value="BALL"/>
</group_2>
</Main_group>
<SubGroup>
<add key="A" value="AMERICA"/>
<add key="B" value="BALL"/>
</SubGroup>
</xml>
Here's a bat file that edit his values (should be in the same directory).Without need of installing external programs and the xml is parsed as xml without using regex. You can change the hardcoded values:
#if (#X)==(#Y) #end /* JScript comment
#echo off
cscript //E:JScript //nologo "%~f0"
exit /b %errorlevel%
#if (#X)==(#Y) #end JScript comment */
//------------------------
// -- hard coded values --
var originalFile="xml.txt";
var newFile="xml1.xml";
var xpath1='//SubGroup/add[#key="A"]/#value';
var xpath2='//SubGroup/add[#key="B"]/#value';
var newValue1="USA";
var newValue2="Basket Ball";
//---------------------------
var objDoc;
var objNodes;
var loaded;
try {
objDoc = WScript.CreateObject("MSXML.DOMDocument");
loaded=objDoc.load(originalFile);
} catch (err){
WScript.Echo("Error while parsing the xml");
WScript.Echo(err.message);
WScript.Quit(1);
}
if(!loaded){
WScript.Echo("Error while parsing the xml");
WScript.Echo("");
WScript.Echo("Error Code:"+objDoc.parseError.errorCode);
WScript.Echo("");
WScript.Echo("Line:"+objDoc.parseError.line+" Posotion:"+objDoc.parseError.filepos);
WScript.Echo("");
WScript.Echo("Reason:"+objDoc.parseError.reason);
WScript.Echo("");
WScript.Echo("URL:"+objDoc.parseError.url);
WScript.Echo("");
WScript.Echo(objDoc.parseError.srcText);
WScript.Quit(5);
}
var node1=objDoc.selectSingleNode(xpath1);
var node2=objDoc.selectSingleNode(xpath2);
node1.text=newValue1;
node2.text=newValue2;
objDoc.save(newFile);

You probably want to use xmlstarlet to handle XML documents.
Here is the link to install it:
http://xmlstar.sourceforge.net/download.php
After getting it working, here is the code to handle the required changes:
xmlstarlet ed [--inplace] -u '/xml/SubGroup/add[#key="A"]/#value' -v "USA" -u '/xml/SubGroup/add[#key="B"]/#value' -v "Basket Ball" changexml.xml
The argument --inplace will change the original file.

If you want to use sed on Windows, you have to get a Unix environment like MSYS, Cygwin or Windows Subsystem for Linux.
However, it's not a good idea to parse XML with RegEx.

Related

Wix:CustomAction failure in action immediate or deferred either

Environment: Windows 7 x64, Wix Toolset 3.10
Hello
I cannot call cmd.exe by Custom Action through Execute attribute "immediate" or "deferred" in either way.
Runtime Error always happen.
I can call vbs command by a Custom Action, but cannot fire .bat file,
What's wrong? I suppose there might be inconsistency between the path of input and output, but I cannot find it.
If I can output the log files with only English, please teach me....
In deferred action, log InstallInitialize, InstallFinalize might suggest me to do something with Orca, but I am not sure what I should do.
Below is detail of my attempt:
Following a sample suggested in https://alexanderst.wordpress.com/2010/10/28/wix-how-to-run-application-with-parameters-from-custom-action/
My definition of a property for cmd.exe below:
<Property Id="CMD">
<DirectorySearch Id="CmdFolder" Path="[SystemFolder]" Depth="1">
<FileSearch Id="CmdExe" Name="cmd.exe" />
</DirectorySearch>
</Property>
My definition of a directory for a batch file below:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="FugaFuga" Name="FugaFuga">
<Directory Id="INSTALLDIR" Name="Hoge 1.0">
<Component Id="FugaFugaLibrary" Guid="MY-UID">
<File Id="test.bat" Name="test.bat" DiskId="1" Source="test.bat" KeyPath="yes" />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
My definition of the Publish Event DoAction is below:
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next(&N)">
<Publish Event="DoAction" Value="CallCmdTest">1</Publish>
<Publish Event="NewDialog" Value="NextDlg" />
</Control>
test.bat content is below (both of hoge.txt and temp\hoge folder exist)
copy /Y C:\temp\hoge\hoge.txt C:\temp\hoge\hoge%date:~-10,4%%date:~-5,2%%date:~-2,2%.txt
It simply copys the hoge.txt to a hoge{%date% without slash}.txt
I tried Execute="deferred" but Runtime Error happens when the next button is pushed.
<CustomAction Id="CallCmdTest" Property="CMD" Return="check" Execute="deferred" Impersonate="yes" ExeCommand="/c ""[#test.bat]""" />
Its log file (I'm sorry for the japanese characters...)(msiexec /i Main.msi /lv* Main-install.log result:) Error 2762 but return code is 3...
アクション開始 11:04:22: CallCmdTest。
MSI (c) (08:C0) [11:04:22:176]: Note: 1: 2762
DEBUG: Error 2762: Unable to schedule operation. The action must be scheduled between InstallInitialize and InstallFinalize.
このパッケージをインストールしているときに、インストーラーに予期しないエラーが発生しました。このパッケージに問題がある可能性があります。エラー コードは、2762 です。引数: 、、
MSI (c) (08:C0) [12:03:24:486]: 製品: HogeHoge 1.12.0 -- このパッケージをインストールしているときに、インストーラーに予期しないエラーが発生しました。このパッケージに問題がある可能性があります。エ ラー コードは、2762 です。引数: 、、
アクション終了 12:03:24: CallCmdTest。 戻り値 3。
I tried Execute="immediate" but a Runtime Error happens either when the next button is pushed:
<CustomAction Id="CallCmdTest" Property="CMD" Return="check" Execute="immediate" ExeCommand="/c ""[#test.bat]""" />
Its log file (I'm sorry for the japanese characters...)(msiexec /i Main.msi /lv* Main-install.log result:) Error 2762 but return code is 3..
アクション開始 17:13:23: CallCmdTest。
MSI (c) (B8:C4) [17:13:24:034]: Note: 1: 1722 2: CallCmdTest 3: C:\Windows\SysWOW64\cmd.exe 4: /c ""C:\Program Files (x86)\HogeHoge\Hoge 1.0\test.bat""
エラー 1722。この Windows インストーラー パッケージに問題があります。セットアップの一部として実行されるプログラムが正常に完了しませんでした。サポート担当者またはパッケージ開発元に問い合わせてください。アクション CallCmdTest、場所: C:\Windows\SysWOW64\cmd.exe、コマンド: /c ""C:\Program Files (x86)\HogeHoge\Hoge 1.0\test.bat""
MSI (c) (B8:C4) [17:13:25:694]: 製品: HogeHoge 1.12.0 -- エラー 1722。この Windows インストーラー パッケージに問題があります。セットアップの一部として実行されるプログラムが正常に完了しませんでした。サポート担当者またはパッケージ開発元に問い合わせてください。アクション CallCmdTest、場所: C:\Windows\SysWOW64\cmd.exe、コマンド: /c ""C:\Program Files (x86)\HogeHoge\Hoge 1.0\test.bat""
アクション終了 17:13:25: CallCmdTest。 戻り値 3。
DEBUG: Error 2896: Executing action CallCmdTest failed.
このパッケージをインストールしているときに、インストーラーに予期しないエラーが発生しました。このパッケージに問題がある可能性がありま す。エラー コードは、2896 です。引数: CallCmdTest、、
アクション終了 17:13:25: WelcomeDlg。 戻り値 3。
MSI (c) (B8:B8) [17:13:25:716]: Doing action: FatalError
アクション 17:13:25: FatalError。
All files (Main.wxs, Main.wixobj, Main.msi, test.bat) exist in one folder.
All commands of candle, light, msiexec are executed in the same directory.
cd /d %~dp0
candle.exe Main.wxs
light.exe -cultures:ja-jp -ext WixUIExtension -out Main.msi Main.wixobj
msiexec /i Main.msi /lv* Main-install.log
As Gerhard Barnard metioned, using a custom action is probably the right way to do this. It looks like you are trying to trigger your custom action from the UI which
Can't be deferred as it is happening from the UI
Can't have elevation unless the entire installer was launched elevated
Won't be able to access the bat file if you haven't actually installed the product yet.
I would instead rewrite your bat file as a c# or c++ custom action and include that in your installer following the instructions in the link in Gerhard's comment and other SO questions.
Additionally, Custom actions launched from a UI event can't write logs easily to the msi log (or at all?). If you schedule your custom action appropriately you will be able to do informative logging about what your custom action is doing. You can also debug your custom action if necessary. In the past I've shown a message box at the start of the custom action then attached a debugger for debugging.

How to access local env variables from ANT file

I have this ANT file that calls a batch file to set the environment variables, before these environment variables are referred to in the ANT script.
So my batch file (set_env.bat) contains:
REM ------ ECLIPSE ------
SET DIR_ECLIPSE=C:\Eclipse
SET DIR_ECLIPSE_MARS=%DIR_ECLIPSE%\eclipse-dsl-mars-2-win32
SET DIR_DEST=C:\mydsl\generated
Then my ANT script will call it like this:
<target name="setvars" >
<exec executable="cmd">
<arg value="/c"/>
<arg value="set_env.bat"/>
<arg value="-p"/>
</exec>
</target>
<property environment="env"/>
<property name="tool" value="${env.DIR_ECLIPSE_MARS}" />
<property name="destDir" value=${env.DIR_DEST}" />
<target name="create_run_jar">
<copy file="${env.DIR_ECLIPSE_MARS}/plugins/org.eclipse.xtext_2.9.1.v201512180746.jar"
todir="${destDir}/myDsl_lib" />
</target>
I keep on getting an error saying the file cannot be found and when I print the log via , I get "[echo] ${env.DIR_ECLIPSE_MARS}". So this means that the env vars are not set.
During copy, it displays this log:
D:\mydsl\antbuild.xml:129: Warning: Could not find file D:\mydsl\${env.DIR_ECLIPSE_4_5_2}\plugins\org.eclipse.xtext_2.9.1.v201512180746.jar to copy.
I execute the setvars target first followed by the create_run_jar.

List of files located in text file to be send as parameter to WinSCP in script mode

I've to create solution that is based on WinSCP and windows batch script. Below are tasks the script has to do:
Save list of files from remote directory, to file located on machine where batch script is run.
Run WinSCP in command mode (/command) using WinSCP script passed as parameter (/script=name_of_script_file.txt) and get files (get command) from previously generated list.
The most important is to get file list, save it and pass names of files located in created file to WinSCP to get them.
How to implement this?
There's no easy way to implement this using WinSCP scripting only. It's possible, see the very end of my answer, but it may not be an ideal solution.
Why do you do this in two steps? Why don't you directly download the directory?
winscp.com /command ^
"option batch abort" ^
"option confirm off" ^
"open scp://user:password#example.com/" ^
"get /path/*" ^
"exit"
If you really need the list (e.g. for some further processing), you can instead of getting a list of files in the directory, get a list of actually downloaded files.
Enable the XML logging, and get the list from the XML log.
winscp.com /xmllog=log.xml /command ^
....
You get a log like:
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="user#host" start="2015-01-30T06:45:57.008Z">
<download>
<filename value="/path/file1.txt" />
<destination value="C:\path\file1.txt" />
<result success="true" />
</download>
<download>
<filename value="/path/file2.txt" />
<destination value="C:\path\file2.txt" />
<result success="true" />
</download>
</session>
If you need a plain-text list, you can use the XSLT to convert it (e.g. download.xslt):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:winscp="http://winscp.net/schema/session/1.0">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match='winscp:download[winscp:result[#success="true"]]/winscp:filename'>
<xsl:value-of select="#value"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
To process it, use any XSLT processor:
Microsoft msxsl.exe (deprecated, but available from Internet Archive)
msxsl.exe log.xml download.xslt
Libxml2 xsltproc.exe:
xsltproc.exe download.xslt log.xml
And you get:
/path/file1.txt
/path/file2.txt
See Transforming XML Log to Text Output Using XSLT Transformation.
To answer your actual question. If you really insist on getting a file list and downloading files according to it, I suggest you use the WinSCP .NET assembly from a PowerShell script.
Use the Session.ListDirectory to retrieve a contents of a remote directory.
See the Session.ListDirectory documentation and its example code.
For recursive listing, you can also use the Session.EnumerateRemoteFiles.
Iterate the results and call the Session.GetFiles for every file.
See the Session.GetFiles documentation and its example code.
If the simple scripting is your preference:
The only way to obtaining a plain text list of remote files reliably, is to use the XML logging to capture an output of the ls scripting command:
winscp.com /xmllog=log.xml /command ^
"option batch abort" ^
"option confirm off" ^
"open scp://user:password#example.com/" ^
"ls /path" ^
"exit"
You get a log like:
<?xml version="1.0" encoding="UTF-8"?>
<session xmlns="http://winscp.net/schema/session/1.0" name="user#host" start="2015-01-30T07:08:27.749Z">
<ls>
<destination value="/path" />
<files>
<file>
<filename value="." />
<type value="d" />
<modification value="2014-07-28T07:06:49.000Z" />
<permissions value="rwxr-sr-x" />
</file>
<file>
<filename value=".." />
<type value="d" />
<modification value="2015-01-23T12:22:44.000Z" />
<permissions value="rwxr-xr-x" />
</file>
<file>
<filename value="file1.txt" />
<type value="-" />
<size value="1306091" />
<modification value="2015-01-29T23:58:12.000Z" />
<permissions value="rw-rw-rw-" />
</file>
<file>
<filename value="file2.txt" />
<type value="-" />
<size value="88" />
<modification value="2007-11-17T22:40:43.000Z" />
<permissions value="rw-r--r--" />
</file>
</files>
<result success="true" />
</ls>
</session>
Again, use the XSLT to convert the XML log to a plain-text list of files:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:winscp="http://winscp.net/schema/session/1.0">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match='winscp:file[winscp:type/#value="-"]/winscp:filename'>
<xsl:value-of select="#value"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
You get:
file1.txt
file2.txt
To get WinSCP script to download files according to a plain-text list, see scripting example Uploading a list of files. It's for an upload, but just replace the put with the get and reverse an order of arguments to turn it to a download.
Note that the /command parameter does not enter a "command" (scripting?) mode. It's for passing scripting commands on a command line (as used in my answer). You are using a script file (/script). There's no point adding an empty /command parameter to it.
See the documentation of WinSCP scripting command-line parameters.

Wix: use computername in custom action directory

I want to use the name of the computer where the msi is running in a customaction to execute a batch file that is deployed with the msi file in a directory that is the same as the computername.
For example, after installation of the msi on a machine named: 'MyServer' the directory looks like: C:\program files\MyApp\Config\MyServer\Config.bat
I want to run the Config.bat file after installation, so i came up with custom action
<CustomAction
Id="CONFIGURE"
Directory="INSTALLFOLDER\Config\[COMPUTERNAME]"
ExeCommand="Configure.bat"
Execute="deferred"
Return="ignore" />
<InstallExecuteSequence>
<Custom Action="CONFIGURE"
After="InstallFiles" />
</InstallExecuteSequence>
Now i need the real computername instead of the [COMPUTERNAME].
Or something like this:
<CustomAction
Id="CONF"
Directory="INSTALLFOLDER"
ExeCommand="\Config\[COMPUTERNAME]\Configure.bat"
Execute="deferred"
Return="ignore"
HideTarget="no"
Impersonate="no" />
The above examples looks better, but at installation the log file tells me:
Action: CONF, location: c:\Program Files (x86)\MyApp\, command: \Config\\Configure.bat
So the computername is empty...
+++
Finally thanks to the one Dusan Plavak, the solution is:
<CustomAction
Id="CONF"
Directory="INSTALLFOLDER"
ExeCommand="\Config\[ComputerName]\Configure.bat"
Execute="deferred"
Return="ignore"
HideTarget="no"
Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="CONF"
After="InstallFiles" />
</InstallExecuteSequence>
You should use ComputerName instead of COMPUTERNAME
so for example you can copy ComputerName to COMPUTERNAME name using custom action"
<CustomAction Id="SetMachineName" Property="COMPUTERNAME" Value="[ComputerName]" Execute="immediate"></CustomAction>

WIX invoking Batch file in InstallExecution Sequence

I am new to WIX and I am trying to invoke a batch file from my WIX installer.
MyBatch.bat contains a simple Copy of a Text file from c:\ to D:\
I am unable to invoke the batch file. Below is my code.
Please help me out.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ProductName='Test Product' ?>
<?define ProductVersion='1.0.0.0' ?>
<?define ProductCode='b7bc7c6f-9a4e-4973-be84-eca8e3427c97'?>
<?define UpgradeCode='06a81104-1e30-463d-87e1-e8a79b4c682a'?>
<?define Manufacturer='Respond'?>
<Product Id="$(var.ProductCode)" Language="1033" Version='$(var.ProductVersion)' Name='Respond'
Manufacturer='$(var.Manufacturer)' UpgradeCode='$(var.UpgradeCode)'>
<Package InstallerVersion='200' Compressed='yes' />
<Media Cabinet='media1.cab' EmbedCab='yes' Id ='1' />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id='ProgramFilesFolder'>
<Directory Id='INSTALLDIR' Name='$(var.ProductName)'>
<Component Id='ProductComponent' Guid='b11556a2-e066-4393-af5c-9c9210187eb2'>
<File Id='Calc' DiskId='1' Source='C:\WINDOWS\system32\calc.exe'/>
<File Id='my_batch_script' Name='MyBatch.bat' DiskId='1' Source='MyBatch.bat' />
</Component>
</Directory>
</Directory>
<Directory Id='ProgramMenuFolder'>
<Directory Id='ApplicationProgramsFolder' Name='$(var.ProductName)'>
<Component Id='MainExecutable' Guid='EDED00D8-2465-46CA-86D6-B20DE921EFA6'>
<Shortcut Id='ShortcutCalc' Description='$(var.ProductName)' Name ='Calculator of Windows'
Target='[INSTALLLOCATION]Calc.exe' WorkingDirectory='INSTALLLOCATION'/>
<RemoveFolder Id='ApplicationProgramsFolder' On='uninstall'/>
<RegistryValue Root='HKCU' Key='Software\$(var.Manufacturer)\$(var.ProductName)' Type ='multiString'
Name='installed' Value='1' KeyPath='yes'/>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Title='SetupProject1' Level='1' Id ='AppFeature'>
<ComponentRef Id='ProductComponent' Primary='yes'/>
<ComponentRef Id='MainExecutable'/>
</Feature>
<Property Id='WIXUI_INSTALLDIR' Value='INSTALLDIR' ></Property>
<WixVariable Id='WixUILicenseRtf' Overridable='yes' Value='License.rtf'/>
<?include CustomActions.wxi?>
<UI>
<?include UISequence.wxi?>
<?include Dialogs.wxi?>
</UI>
<CustomAction Id="BatchCmd" Property="ThePath" Value="[INSTALLDIR]MyBatch.bat" />
<CustomAction Id="BatchRun" Property="ThePath" ExeCommand='"[INSTALLDIR]MyBatch.bat"' Return='asyncWait' Impersonate="yes"/>
<InstallExecuteSequence>
<Custom Action="BatchCmd" After="InstallFiles" >Not Installed </Custom>
<Custom Action="BatchRun" After="BatchCmd">Not Installed </Custom>
</InstallExecuteSequence>
</Product>
</Wix>
This worked for me.
<CustomAction Id="test"
ExeCommand="[INSTALLDIR]MyBatch.bat"
Directory="INSTALLDIR" Execute="deferred" Return="asyncWait"/>
<InstallExecuteSequence>
<Custom Action="test" After="InstallFiles">NOT Installed</Custom>
</InstallExecuteSequence>
It seems you're solving your problem with the wrong tool. If the file to copy is a part of your application, you should author a Component with this File inside. Otherwise, if you need just to copy some external file already present on a target system, you should author a CopyFile element, nesting it under Component element.
I agree with Yan, you solve your task the wrong way.
As for batch files, they are not executed on its own like an .exe, it is cmd.exe which reads the bat file and executes it line by line.
To run a batch file, you should run it this way:
cmd.exe /c batch.bat
Add your CustomAction defnition as given below. Make sure that Return="ignore" if you are not returning anything from bat file.
<CustomAction Id="RunBat" Directory="your_directory"
ExeCommand='"c:\test\test.BAT"'
Execute='deferred' Impersonate='no' Return='ignore'/>
Also in the installExecuteSequence sequence add the action before InstallFinalize
<InstallExecuteSequence>
<Custom Action="RunBat" Before="InstallFinalize">
NOT (REMOVE~="ALL")
</Custom>
</InstallExecuteSequence>

Resources