Sujit.Warrier
Sujit.Warrier

Reputation: 2889

Can't concatenate value of 2 variables in batch script

I have created a batch file that reads a .json file containing npm packages and their version.

Now everything works except concatenating values in 2 variables before writing to file

This is the InstallPackage.json file:

{
  "dependencies": {
    "@types/react": "16.3.10",
    "react": "16.3.2",
    "react-dom": "16.3.2",
    "react-scripts": "1.1.4"
  },
  "devdependencies":{}
}

This is the batch file:

<# : batch portion (contained within a PowerShell multi-line comment)
@echo off & setlocal
setlocal EnableDelayedExpansion
set LF=^


set JSON=
for /f "delims=" %%x in (InstallPackage.json) do (
    set "JSON=!JSON!%%x!LF!"
)

rem # re-eval self with PowerShell and capture results
for /f "delims=" %%I in ('powershell "iex (${%~f0} | out-string)"') do set "%%~I"

rem # output captured results
set JSON[

rem # end main runtime
goto :EOF

: end batch / begin PowerShell hybrid code #>

add-type -AssemblyName System.Web.Extensions
$JSON = new-object Web.Script.Serialization.JavaScriptSerializer
$obj = $JSON.DeserializeObject($env:JSON)

# output object in key=value format to be captured by Batch "for /f" loop
foreach ($key in $obj.keys) { 
    foreach($package in $obj[$key].keys){

###     error happens here ###

        echo $package@$obj[$key][$package] >> packages.txt
    }
}

instead of getting

@types/[email protected]

I get:

@types/[email protected]`2[System.String,System.Object][dependencies][@types/react]

It's a simple concatenate don't know why it's not working.

Upvotes: 1

Views: 189

Answers (2)

Reino
Reino

Reputation: 3443

So much code for something so simple. At least for the JSON-parser :

xidel -s InstallPackage.json^
  -e "let $obj:=$json/dependencies return $obj() ! `{.}@{$obj(.)}`"^
  > packages.txt

'package.txt':

@types/[email protected]
[email protected]
[email protected]
[email protected]

Upvotes: 0

rojo
rojo

Reputation: 24486

Your PowerShell output line isn't quite PowerShell-ese. It's sort of PowerCmd or something. Replace

echo $package@$obj[$key][$package] >> packages.txt

with

"JSON[{0:d}]={1}@{2}" -f $i++, $package, $obj[$key][$package] >> packages.txt

... if you want to populate the JSON[] array when PowerShell exits back to the Batch environment, or

"{0}@{1}" -f $package, $obj[$key][$package] >> packages.txt

... if you only want the concatenated values sent to packages.txt. And if that's the case, get rid of the for /F loop within Batch. You can also use gc within PowerShell to read the JSON, which is much more graceful than trying to populate a Batch variable with a multi-line value.

<# : batch portion (contained within a PowerShell multi-line comment)
@echo off & setlocal

set "JSON=InstallPackage.json"

rem # re-eval self with PowerShell and capture results
powershell -noprofile "iex (${%~f0} | out-string)"

rem # end main runtime
goto :EOF

: end batch / begin PowerShell hybrid code #>

add-type -AssemblyName System.Web.Extensions
$JSON = new-object Web.Script.Serialization.JavaScriptSerializer
$obj = $JSON.DeserializeObject((gc $env:JSON))

# output object in key@value format to packages.txt
foreach ($key in $obj.keys) { 
    foreach($package in $obj[$key].keys){
        "{0}@{1}" -f $package, $obj[$key][$package] >> packages.txt
    }
}

And I've got one more recommendation. Instead of writing packages.txt using the append redirect, your intentions would be clearer if you use the out-file cmdlet. Do you expect packages.txt to be created from scratch on every run? Or will packages.txt continue to grow with multiple runs? If you revisit this code a year from now, will you remember your intentions?

This would be clearer:

# output object in key@value format to packages.txt
&{
    foreach ($key in $obj.keys) { 
        foreach($package in $obj[$key].keys){
            "{0}@{1}" -f $package, $obj[$key][$package]
        }
    }
} | out-file packages.txt

If your intention is to grow packages.txt with successive runs, then use

out-file packages.txt -append

See? This way, packages.txt will explicitly be either overwritten or appended on each run, and will not require any checks whether the file already exists or expectations that the file will not already exist. It's less ambiguous this way.

Upvotes: 1

Related Questions