Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

Multi-line UDFs

May
12,846
164
[A bit of smoke and mirrors here!]

When you load functions with FUNCTION /R line continuations (^) are respected. This lets you (at least in a sense) define multi-line UDFs (example far below).

Notes:

- I believe the rules are line-continuation removes the newline and leading whitespace on the next line
- Anything inside %@exec[@ ...] is executed and the '@' suppresses @EXEC's return value (it returns an empty string).
- @EXEC is probably limited by the command line length limit (which is at worst huge, and today, perhaps, equal to available memory).
- I threw in the ECHOs (with redirection and piping) and the DO only to show that any (so far) one-line command can be used in @EXEC.
- You probably wouldn't want something in @EXEC to produce output.
- In memory, the function winds up on one line.

Code:
d:\tc29> type secs2dhms.fn
secs2dhms=^
%@exec[@ ^
    set _x=%1 ^
    & set _d=%@eval[%_x\86400] ^
    & set _x=%@eval[%_x MOD 86400] ^
    & set _h=%@eval[%_x\3600] ^
    & set _x=%@eval[%_x MOD 3600] ^
    & set _m=%@eval[%_x\60] ^
    & set _x=%@eval[%_x MOD 60] ^
    & echo foo > NUL ^
    & echos foo | findstr bar ^
    & do i=1 to 2 (echo foo > NUL) ^
]^
%_d:%@format[02,%_h]:%@format[02,%_m]:%@format[02,%_x]

d:\tc29> function /r secs2dhms.fn

d:\tc29> echo %@secs2dhms[90000]
1:01:00:00

d:\tc29> function secs2dhms
%@exec[@ set _x=%1 & set _d=%@eval[%_x\86400] & set _x=%@eval[%_x MOD 86400] & set _h=%@eval[%_x\3600] & set _x=%@eval[%_x MOD 3600] & set _m=%@eval[%_x\60] & set _x=%@eval[%_x MOD 60] & echo foo > NUL & echos foo | findstr bar & do i=1 to 2 (echo foo > NUL) ]%_d:%@format[02,%_h]:%@format[02,%_m]:%@format[02,%_x]
 
Here's another strategy. Make a library function (below, s2dhms) to do the dirty work and set (and export from SETLOCAL) the variable LIBRESULT, which will be the value of the UDF. A batch file works too; I'm using a library function so everything is in memory.

Code:
d:\data\tcclibrary> type s2dhms_source.btm
s2dhms {
setlocal
set _x=%1
set _d=%@eval[%_x\86400]
set _x=%@eval[%_x MOD 86400]
set _h=%@eval[%_x\3600]
set _x=%@eval[%_x MOD 3600]
set _m=%@eval[%_x\60]
set _x=%@eval[%_x MOD 60]
set libresult=%_d:%@format[02,%_h]:%@format[02,%_m]:%@format[02,%_x]
endlocal libresult
}

Load the library function (done automatically at startup if it's in TCC_home\library).

Code:
d:\data\tcclibrary> library /r s2dhms_source.btm /u

Define the UDF like this.

Code:
function test `%@exec[@setlocal & s2dhms %1]%[libresult]%@exec[@endlocal]`

[You could @EXEC a BTM instead of a library function.]

The SETLOCAL/ENDLOCAL in the UDF definition gets rid of the LIBRESULT variable.

Try it.

Code:
d:\data\tcclibrary> echo %@test[90061]
1:01:01:01
 
Another example. I'm actually using this one.

I wanted to get the state of %svcname (a Windows service) and I didn't want a Pending state. This works.

Code:
v:\> function getstate `%@exec[@set ns=Pending & do while %@index[%ns,Pending] != -1 (set ns=%@wmi[.,"Select State from Win32_Service where Name='%1'"])]%ns`

v:\> echo %@getstate[appxsvc]
Running
 
Back
Top