The important thing for such, as I call them, nested variables is that the inner ones need to be replaced/expanded before the outer ones. Variables a and b are the ones I consider as the inner ones, and file3 is the outer one.
So using delayed expansion, we have two expansion phases available: the normal or immediate expansion (%) and the delayed expansion (!). Applying them in the said order the solution looks like this:
echo !%a%%b%!
So at first %a% is expanded to file and %b% to 3, hence the delayed expansion phase receives !file3!.
There is also a method that avoids delayed expansion: using call, which introduces another parsing phase where immediate expansion is repeated. The code looks like this:
call echo %%%a%%b%%%
So during the first normal expansion phase %% becomes replaced by a literal %, %a% becomes expanded to file, %b% to 3 and the remaining %% becomes again replaced by %; so we have %file3% left, which is expanded by the said second parsing phase due to call.
Both of the above methods imply normal expansion for the inner variables, which can cause problems when they are changed in a parenthesised block of code, like loop, for example. To avoid normal expansion we could use a for loop meta-variable, which is expanded later than normal expansion but before delayed expansion:
for %%I in ("!a!!b!") do echo !%%~I!
Or:
for /F "delims=" %%I in ("!a!!b!") do echo !%%I!
Since no immediate expansion is used herein, this would also work within another for loop even with changing variables a and b.