With more recent versions, yes. The user didn't state what version Ensemble was in use, so I figured offering a solution that would work as far back as Ensemble 2009 wouldn't necessarily be a bad thing. I still have to support Ensemble 2012 and I did test the *-1 functionality there, and in that version this functionality did not yet exist. (Actually, a few sites I support are still using Cache 5.x. It may be geriatric, but it keeps on plugging along... :-) )

The $LENGTH function can give you the number of PIECEs in a string, and the $PIECE function can grab multiple pieces, so for your example:

S DELIM="D"
S STRING="aaDtext1Dtext2"
W $PIECE(STRING,DELIM,1,($LENGTH(STRING,DELIM)-1))

Edit: Links to documentation for the $PIECE and $LENGTH functions:

$PIECE: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fpiece

$LENGTH: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_flength

Hope this helps!

If you're asking what to do to only have one double-quote in the result, you could use this:

s b="""Cat"
w b

Basically, wherever you want a double-quote in the output string, put two double-quotes together in the string. So, if you wanted the string to say: Cat"so"only"one you would use this:

s b="Cat""so""only""one"

When I write code that needs to output comma separated value files, I often create variables to hold both the double-quote and comma characters, to me the resulting code is much easier to read. For example:

S Q="""",C=",",QCQ=Q_C_Q ; Sets Q to " char, C to comma, and QCQ to ","
; so when I need to output some strings, I just use the variables:
W Q_"string1"_QCQ_"string2"_QCQ_"string3"_Q,!

To me, it's easier to read than a whole bunch of imbedded double-quotes all strung together.

Hope this helps!

I'm hoping this will be enough to at least "get you started" - so here's a programming example that will handle either a single or dual \x notation code:

ZUNICODE ; DECODE HEX/UNICODE SEQUENCES IN A STRING
 Q
 ;
CVT(TEXT) ; ENTRY POINT FOR EXTRINSIC FUNCTION TO CONVERT HEX CODE(S)
 ; IN A STRING. ONLY CONVERTS FIRST SET - IMPROVEMENT OF
 ; ROUTINE LEFT AS AN EXERCISE TO THE READER.
 N P1,P2,C1,C2,OUT
 I $L(TEXT,"\x")=1 W "NO USABLE HEX MARKERS FOUND - DID NOT CONVERT TEXT.",! Q TEXT
 I $L(TEXT,"\x")=2 W "SINGLE HEX MARKER FOUND. CONVERTING.",! D
 . S P1=$P(TEXT,"\x",1)
 . S P2=$P(TEXT,"\x",2)
 . S C1=$E(P2,1,2)
 . S P2=$E(P2,3,$L(P2))
 . S OUT=P1_$C($ZHEX(C1))_P2
 I $G(OUT)'="" Q OUT
 I $L(TEXT,"\x")=3 W "DUAL UNICODE MARKER FOUND. CONVERTING.",! D
 . S P1=$P(TEXT,"\x",1)
 . S C1=$P(TEXT,"\x",2)
 . S P2=$P(TEXT,"\x",3)
 . S C2=$E(P2,1,2)
 . S P2=$E(P2,3,$L(P2))
 . S OUT=P1_$C($ZHEX(C1_C2))_P2
 I $G(OUT)'="" Q OUT
 W "MORE THAN TWO UNICODE MARKERS FOUND - LOOPS WOULD HELP. DID NOT CONVERT TEXT.",!
 Q TEXT

Here's some examples on how it's called:

USER>S BB="Hello World"
 
USER>S BB="Hello \xc2\xa3 World"
 
USER>S OUT=$$CVT^ZUNICODE(BB)
DUAL UNICODE MARKER FOUND. CONVERTING.
 
USER>W OUT
Hello 슣 World
USER>S BB="Hello \xc2 World"
 
USER>S OUT=$$CVT^ZUNICODE(BB)
SINGLE HEX MARKER FOUND. CONVERTING.
 
USER>W OUT
Hello  World
USER>S BB="Hello \xa3 World"
 
USER>S OUT=$$CVT^ZUNICODE(BB)
SINGLE HEX MARKER FOUND. CONVERTING.
 
USER>W OUT
Hello £ World

USER>S BB="Hello World"
 
USER>S OUT=$$CVT^ZUNICODE(BB)
NO USABLE HEX MARKERS FOUND - DID NOT CONVERT TEXT.
 
USER>W OUT
Hello World

Now, the output above seems to show that the pound symbol is a single hex character \xa3, at least on the Windows system that I am using. Maybe the \xa3 means "special character coming next" as at least in the charmap utility on Windows Server 2016 shows the U+C2A3 character as "Hangul Syllable Sios Yu Hieuh" (font: Gulim) and on my Ubuntu Linux 20.04 system it says "HANGUL SYLLABLE SYUH" (fonts: Trebuchet MS and Noto Serif CJK SC, pasted into LibreOffice Writer).

There are limitations to this code that could be improved. For one example, it doesn't handle multiple codes (single or unicode)... but hopefully this will give you (if nothing else) a good troubleshooting tool to get you started.

First, just to let you know that even back with Cache 2017.x.x, $ZF(-1) & $ZF(-2) are deprecated and shouldn't be used for new code. $ZF(-100) should be used instead. As $ZF(-100) is more powerful anyway, I would suggest reading up on it:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fzf-100

With $ZF(-100) you can redirect input and output to files or other devices which may help you run the task you're requiring in the background. Note that I don't know (at least in a Windows environment) if you can successfully run a job in the background and still have interactive access to that process. There's a note in the documentation above that states that /ASYNC jobs use the null device for input if it's not redirected from a file. I don't know if you can redirect from a device instead.

Hope this helps!

There are provisions in the $ZDATEH function to cover the "two-digit-year" century issue. If you need all two-digit years to equate to 20xx, try this:

$ZDATEH("01/01/23",,,3,58074,94598)

As in, set the 'yearopt' parameter to 3, then set the startwin & endwin dates (in $H format) to the beginning and ending window to interpret the two-digit years.

If you need a specific range 100 year range - for this example, to evaluate two-digit years between 01/01/1950 and 12/31/2049, you'll need the $H values of those dates:

W $ZDATEH("1/1/1950")
39812
W $ZDATEH("12/31/2049")
76336

Then use those $H values in the window of the $ZDATEH command:

$ZDATEH("01/01/23",,,3,39812,76336)

Here's more documentation on the $ZDATEH command:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_fzdateh

Hope this helps!

One option would just be a straight telnet session (swap 10.10.10.10 with the IP address of your system):

telnet 10.10.10.10 1972

If the port is closed, you should get the error: "telnet: Unable to connect to remote host: Connection refused" - but if successful you should get the "Connected to 10.10.10.10" you'll know it's open. To exit, type <CTRL>] for a telnet prompt, then type 'quit'.

Hope this helps!

Robert,

I'm not sure if this helps your particular situation, but if it's "OK" to manually tell your container what the local hostname of the host is, then you could try this:

set ip=$SYSTEM.INetInfo.HostNameToAddr("**local_name_NotFQDN**")

with just the local hostname (not the FQDN) of the host machine in quotes... and that should give you the IP address of the active ethernet/wireless adapter. I tried this in a container running IRIS on a Raspberry Pi and the local hostname is "Iris2" (so, it is running Linux, I don't have any container systems running on Windows... sorry!) and this is what I got:

USER> Set ip=$SYSTEM.InetInfo.HostNameToAddr("Iris2")
USER> zw ip
ip="192.168.1.236"

On my network, 172.17.0.x is the internal Docker bridge, 192.168.1.x is my wireless network, and 10.1.2.x is my desktop wired network. (I have many servers, printers & whatnot, so I have multiple VLANs on my home network.) Now... I'm not sure if this is good or bad for your situation, but in my example, if I were to shutdown the container, disable the wireless, hook up an ethernet cable to the network and restart everything, the listed IP from this command would change from the 192.168.1.x to a 10.1.2.x IP address. This could be good if you wanted to know how the main machine was externally connected; or it could be bad if you're using awk/grep/findstr on logs looking for a particular IP. As I said, I'm unsure of your actual use case, so if this had to be portable across several containers and several machines unchanged, this may not help you as you'll manually have to change the machine name in your code.

Hope this helps!

Sure, the $Increment command can also decrement, and the values don't have to be 'just 1.'

Here's the documentation for the $Increment command:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RCOS_FINCREMENT

Simply, you can do this to decrement by 1:

Set TEMP=$INCREMENT(^test,-1)

If you wanted to increment by 10, the command would be this:

Set TEMP=$INCREMENT(^test,10)

To test, try this:

S ^test=0 F  S TEMP=$INCREMENT(^test,10) Q:$G(^test)>100  W ^test,!

Hope this helps!

If you have multiple subscript levels, this may help:

SET I=0,G="^test("""")" FOR {SET G=$QUERY(@G) Q:G=""  SET I=I+1 WRITE G,!} WRITE "Total: ",I,!

Here's the data:

set ^test(1)="aa"
set ^test(1,1,1)="aa"
set ^test(2)="aa"
set ^test(2,1)="aa"
set ^test(2,2,1)="aa"
set ^test(3,1)="aa"
set ^test(4,1)="aa"

And here's the output:

^test(1)
^test(1,1,1)
^test(2)
^test(2,1)
^test(2,2,1)
^test(3,1)
^test(4,1)
Total: 7

If you only wanted the total (especially if the global is much larger) omit the 'WRITE G,!' portion out of the line of code above.

Hope this helps!

Another possibility might be to see if there's been backups of the routines being saved. It won't give you the "who" but it could at least give you the "when" a routine was modified & compiled.

You could use the 'Global Lister' and check the rBACKUP global. Here's an example (you may need to tinker with the the global lister input - taking a deeper dive into the ^rBACKUP global may help):
 

ZZZ>D ^%G
 
Device:
Right margin: 80 =>
Screen size for paging (0=nopaging)? 24 =>
For help on global specifications DO HELP^%G
Global ^rBACKUP("TEST","INT",,0)

or maybe

Global ^rBACKUP("TEST.1","INT",,0)

To really get all the functionality you're looking for, you may need to set up a version control system like git or somesuch.

Hope this helps!

I'm not sure I understand the class examples you have listed, but there's a possibility that you may not need to do this in a class.

Ensemble / HealthShare has a couple different ways that you can send a process output to multiple operations. The first is if you're not using any form of data translation, you can send the output right from the BP configuration screen:

 

Under the Response Target Config Names, I've selected two different targets here, and you can see that the connectivity verifies this - but I'm sending the output to more than two targets! How can that be? Simple, you can also select different targets in the Ensemble Rule Editor - this can be handy if you wanted to apply two different DTL transformations to two different targets. My example is super-simple (as in I'm not applying different rules or DTLs per target) but with multiple Send directives we can specify multiple targets:

 

You can have different rules with different constraints going to different operations - but I just added a send directive to a sample ruleset to show how to configure multiple targets - and as you can see between the two screenshots how Ensemble is sending the data to 4 different targets in two different ways.

Hope this helps!

upgrading to a version where you can safely compact/truncate

If you're not able to upgrade to a newer version, there's still the option of using the ^GBLOCKCOPY utility in the %SYS namespace. Unfortunately, you'll need downtime to use the utility but if you have databases with a high amount of free space, this will make a new copy of a database using the minimal amount of disk space. Then just replace the new database file with the old**, remount the database and Bob's your uncle!

Here's InterSystem's Documentation page for the utility:

https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=GSTU_remote_gblockcopy

Hope this helps!

** P.S. If you did want to save the old database, 7zip is quite good at compressing CACHE.DAT (and IRIS.DAT) database files. Be sure to use the LZMA2 compression method as it works well with multiple threads/cores, and I usually use a 'light' compression level - Fast or Fastest if in windows, -mx=3 if in Linux/Unix.

Julian,

I've tested the method above, and depending on a user's needs, it may not be the best fit. I've run it several times, and I've not seen a lower case character and it's really 'heavy' on the punctuation, some of which may not work well with some websites' password requirements. Here's one example:

ZZZ>W ##CLASS(%PopulateUtils).VarString(120)
%%XDY^F;="#GO=A<B89&K\ZE&3192R8J+9QFO#7J>M0+W=JW%^CL%BGO.1.W1EJ@7Z3,HS0F(<E?UIAE*+[3"CLD$"'\U

Token,

Years ago I created a password generator utility - it's by no means perfect and it's kind of 'tailored' to my use - I do prefer passwords that alternate left hand then right hand to attempt a bit easier manual typing, and even then the randomization doesn't always create a good 'mix' of characters, so I usually have the utility print 10 passwords (or more) and then I choose the 'best' for character mix, punctuation, etc. The routine could be modified for longer passwords, more/different punctuation characters, etc. It's not the _most_ secure, but it works well for my purposes. Feel free to use/modify it, but use it at your own risk. :-)

 PASSWD() ; 
 LEFT,RIGHT,CENTER,LORR,I,J,PASSWD
 LEFT="QAZWSXEDCRFVTGBqazwsxedcrfvtgb234"
 RIGHT="YHNUJMIK,OL.Pyhnujmikolp789"
 CENTER="123456789*-+"
 LORR=$R(2)
 PASSWD=""
 LORR D
 . I=1:1:4 D
 . . PASSWD=PASSWD_$E(LEFT,($R($L(LEFT))+1))_$E(RIGHT,($R($L(RIGHT))+1))
 E  D
 . I=1:1:4 D
 . . PASSWD=PASSWD_$E(RIGHT,($R($L(RIGHT))+1))_$E(LEFT,($R($L(LEFT))+1))
 I=1:1:4 D
 . PASSWD=PASSWD_$E(CENTER,($R($L(CENTER))+1))
 PASSWD
 ;
LOTS(HOWMANY)
 I,J,PASSWD
 +$G(HOWMANY)=0 HOWMANY=10
 ! I=1:1:HOWMANY $$PASSWD,!
 !
 Q
 ;

To run the utility for one password, just execute this:

W $$^PASSWD

But, I generally just print 10 at a time and choose one. To do that, do the LOTS subroutine (which defaults to 10):

ZZZ>D LOTS^PASSWD
 
J3PwIQnX9*76
nzJWPFyt+31+
[[ snippage for brevity ]]

If you want more than 10, add a parameter for the number of passwords you want:

ZZZ>D LOTS^PASSWD(20)

Hope this helps!