As mentioned earlier, there are numerous Slony-I test scripts in src/ducttape that embed the generation of Slonik inside the shell script.
They mostly don't do this in a terribly sophisticated way. Typically, they use the following sort of structure:
DB1=slony_test1 DB2=slony_test2 slonik <<_EOF_ cluster name = T1; node 1 admin conninfo = 'dbname=$DB1'; node 2 admin conninfo = 'dbname=$DB2'; try { table add key (node id = 1, fully qualified name = 'public.history'); } on error { exit 1; } try { create set (id = 1, origin = 1, comment = 'Set 1 - pgbench tables'); set add table (set id = 1, origin = 1, id = 1, fully qualified name = 'public.accounts', comment = 'Table accounts'); set add table (set id = 1, origin = 1, id = 2, fully qualified name = 'public.branches', comment = 'Table branches'); set add table (set id = 1, origin = 1, id = 3, fully qualified name = 'public.tellers', comment = 'Table tellers'); set add table (set id = 1, origin = 1, id = 4, fully qualified name = 'public.history', key = serial, comment = 'Table accounts'); } on error { exit 1; } _EOF_
A more sophisticated approach might involve defining some common components, notably the "preamble" that consists of the CLUSTER NAME ADMIN CONNINFO commands that are common to every Slonik script, thus:
CLUSTER=T1 DB1=slony_test1 DB2=slony_test2 PREAMBLE="cluster name = $CLUSTER node 1 admin conninfo = 'dbname=$DB1'; node 2 admin conninfo = 'dbname=$DB2'; "
The PREAMBLE value could then be reused over and over again if the shell script invokes slonik multiple times. You might also consider using INCLUDE and place the preamble in a file that is included.
Shell variables provide a simple way to assign names to sets and nodes:
origin=1 subscriber=2 mainset=1 slonik <<_EOF_ $PREAMBLE try { table add key (node id = $origin, fully qualified name = 'public.history'); } on error { exit 1; } try { create set (id = $mainset, origin = $origin, comment = 'Set $mainset - pgbench tables'); set add table (set id = $mainset, origin = $origin, id = 1, fully qualified name = 'public.accounts', comment = 'Table accounts'); set add table (set id = $mainset, origin = $origin, id = 2, fully qualified name = 'public.branches', comment = 'Table branches'); set add table (set id = $mainset, origin = $origin, id = 3, fully qualified name = 'public.tellers', comment = 'Table tellers'); set add table (set id = $mainset, origin = $origin, id = 4, fully qualified name = 'public.history', key = serial, comment = 'Table accounts'); } on error { exit 1; } _EOF_
The script might be further enhanced to loop through the list of tables as follows:
# Basic configuration origin=1 subscriber=2 mainset=1 # List of tables to replicate TABLES="accounts branches tellers history" ADDTABLES="" tnum=1 for table in `echo $TABLES`; do ADDTABLES="$ADDTABLES set add table ($set id = $mainset, origin = $origin, id = $tnum, fully qualified name = 'public.$table', comment = 'Table $tname'); " let "tnum=tnum+1" done slonik <<_EOF_ $PREAMBLE try { table add key (node id = $origin, fully qualified name = 'public.history'); } on error { exit 1; } try { create set (id = $mainset, origin = $origin, comment = 'Set $mainset - pgbench tables'); $ADDTABLES } on error { exit 1; } _EOF_
That is of somewhat dubious value if you only have 4 tables, but eliminating errors resulting from enumerating large lists of configuration by hand will make this pretty valuable for the larger examples you'll find in "real life."
You can do even more sophisticated things than this if your scripting language supports things like:
"Record" data structures that allow assigning things in parallel
Functions, procedures, or subroutines, allowing you to implement useful functionality once, and then refer to it multiple times within a script
Some sort of "module import" system so that common functionality can be shared across many scripts
If you can depend on having Bash, zsh, or Korn shell available, well, those are all shells with extensions supporting reasonably sophisticated data structures and module systems. On Linux, Bash is fairly ubiquitous; on commercial UNIX™, Korn shell is fairly ubiquitous; on BSD, "sophisticated" shells are an option rather than a default.
At that point, it makes sense to start looking at other scripting languages, of which Perl is the most ubiquitous, being widely available on Linux, UNIX™, and BSD.