Index: tmp/org.txm.analec.rcp/src/org/txm/macro/urs/AnalecUtils.groovy
===================================================================
--- tmp/org.txm.analec.rcp/src/org/txm/macro/urs/AnalecUtils.groovy	(revision 3223)
+++ tmp/org.txm.analec.rcp/src/org/txm/macro/urs/AnalecUtils.groovy	(revision 3224)
@@ -139,10 +139,8 @@
 	for (def k : groupedUnits.keySet()) {
 		def selectedUnits = AnalecUtils.filterUniteByInclusion(debug, groupedUnits[k], matches, strict_inclusion, position_in_matches)
 
-		if (minimum_schema_size <= selectedUnits.size() && selectedUnits.size() <= maximum_schema_size ) {
+		if (schema_ursql == null || schema_ursql.length() == 0 || (minimum_schema_size <= selectedUnits.size() && selectedUnits.size() <= maximum_schema_size)) {
 			allUnits.addAll(selectedUnits)
-		} else {
-
 		}
 	}
 	if (debug >= 2) println "selectedUnits=${allUnits.size()}"
@@ -151,6 +149,7 @@
 
 	return allUnits
 }
+
 /**
  * filter groups elements with the elements positions
  * 
Index: tmp/org.txm.analec.rcp/src/org/txm/macro/urs/exploit/UnitsIndexMacro.groovy
===================================================================
--- tmp/org.txm.analec.rcp/src/org/txm/macro/urs/exploit/UnitsIndexMacro.groovy	(revision 3223)
+++ tmp/org.txm.analec.rcp/src/org/txm/macro/urs/exploit/UnitsIndexMacro.groovy	(revision 3224)
@@ -50,8 +50,8 @@
 		int maximum_schema_size
 @Field @Option(name="unit_ursql", usage="TYPE@PROP=VALUE", widget="String", required=false, def="MENTION")
 		String unit_ursql
-@Field @Option(name="unit_property_display", usage="Unit property to count", widget="String", required=false, def="CATEGORIE")
-		String unit_property_display
+@Field @Option(name="unit_property_display_list", usage="Unit property to count", widget="String", required=false, def="CATEGORIE")
+		def unit_property_display_list
 @Field @Option(name="word_property_display", usage="Word property to display instead of the unit property", widget="String", required=false, def="")
 		String word_property_display
 @Field @Option(name="position_in_schema", usage="Unit distance in schema (0 = no selection, 1 = first after limit, -1 = last before limit, etc.)", widget="Integer", required=false, def="0")
@@ -74,16 +74,19 @@
 		output_lexicaltable
 @Field @Option(name="debug", usage="Show internal variable content", widget="StringArray", metaVar="OFF	ON	ALL	REALLY ALL", required=false, def="OFF")
 		debug
+		
 if (!ParametersDialog.open(this)) return
 if (debug == "OFF") debug = 0; else if (debug == "ON") debug = 1; else if (debug == "ALL") debug = 2 else if (debug == "REALLY ALL") debug = 3
 
+unit_property_display_list =  unit_property_display_list.split(",") as List
+unit_property_display_list.removeAll("")
 
-if (word_property_display != null && word_property_display.length() > 0 && unit_property_display != null && unit_property_display.length() > 0) {
-	println "Warning: both unit_property_display=$unit_property_display and word_property_display=$word_property_display are set. The index will be computed with the $word_property_display CQP property."
+if (word_property_display != null && word_property_display.length() > 0 && unit_property_display_list != null && unit_property_display_list.size() > 0) {
+	println "Warning: both unit_property_display_list=$unit_property_display_list and word_property_display=$word_property_display are set. The index will be computed with the $word_property_display CQP property."
 }
 
-if (word_property_display.length() == 0 && unit_property_display.length() == 0) {
-	println "Error: no analysis property specified in unit_property_display or word_property_display. Aborting."
+if (word_property_display.length() == 0 && unit_property_display_list.size() == 0) {
+	println "Error: no analysis property specified in unit_property_display_list or word_property_display. Aborting."
 	return
 }
 
@@ -107,7 +110,8 @@
 		return;
 	}
 	
-	if (unit_property_display.length() > 0) {
+	for (def unit_property_display : unit_property_display_list) {
+		println "testing '$unit_property_display'"
 		errors = AnalecUtils.isPropertyDefined(Unite.class, analecCorpus, AnalecUtils.getFilterParameters(unit_ursql)[0], unit_property_display).size()
 		if (errors > 0) {
 			println "Error: some Unit types don't contain the $unit_property_display property: $errors"
@@ -133,10 +137,23 @@
 			counts[s] = counts[s] + 1
 		}
 	} else { // use Analec unit property
-		counts = selectedUnits.countBy { if (it.getProp(unit_property_display) == null) "<null>" else it.getProp(unit_property_display) }
+		counts = selectedUnits.countBy { 
+			def values = []
+			for (def unit_property_display : unit_property_display_list) {
+				
+				if (it.getProp(unit_property_display) == null)  {
+					values << "<null>"
+				} else {
+					values << it.getProp(unit_property_display)
+				}
+			}
+			return values.join(" ")
+		}
 	}
+	
 	if (counts.containsKey("")) counts["<empty>"] = counts[""];
 	counts.remove("")
+	
 	if (debug) {
 		println "\n*** Statistics: "
 		for (def k : counts.keySet()) {
@@ -166,12 +183,12 @@
 def title = "${corpus.getMainCorpus()}.${corpusViewSelection} ${unit_ursql}"
 title += "[${position_in_matches}]."
 if (word_property_display.length() > 0) title += "${word_property_display} frequencies"
-else if (unit_property_display.length() > 0) title += "${unit_property_display} frequencies"
+else if (unit_property_display_list.size() > 0) title += "${unit_property_display_list.join(' ')} frequencies"
 if (cql_limit != null && !cql_limit.getQueryString().equals("\"\"")) title += "\n(${cql_limit} limits)"
 
 if (word_property_display.length() > 0) println "Index de la propriété $word_property_display des mots des unités $unit_ursql[$position_in_matches] de ${corpus.getMainCorpus()}.${corpusViewSelection}"
-else println "Index de la propriété $unit_property_display des unités $unit_ursql[$position_in_matches] de ${corpus.getMainCorpus()}.${corpusViewSelection}"
-println "$unit_property_display\t"+selection.join("\t")
+else println "Index de la propriété ${unit_property_display_list.join(' ')} des unités $unit_ursql[$position_in_matches] de ${corpus.getMainCorpus()}.${corpusViewSelection}"
+println "${unit_property_display_list.join(' ')}\t"+selection.join("\t")
 
 keys.eachWithIndex { prop_val, i ->
 	String line = ""
Index: tmp/org.txm.analec.rcp/src/org/txm/macro/urs/exploit/UnitsListMacro.groovy
===================================================================
--- tmp/org.txm.analec.rcp/src/org/txm/macro/urs/exploit/UnitsListMacro.groovy	(revision 3223)
+++ tmp/org.txm.analec.rcp/src/org/txm/macro/urs/exploit/UnitsListMacro.groovy	(revision 3224)
@@ -1,4 +1,4 @@
-// Copyright © 2016 ENS de Lyon, CNRS, University of Franche-Comté
+// Copyright © 2016, 2021 ENS de Lyon, CNRS, University of Franche-Comté
 // Licensed under the terms of the GNU General Public License (http://www.gnu.org/licenses)
 // @author mdecorde
 // @author sheiden
@@ -62,12 +62,23 @@
 		output_mode
 @Field @Option(name="debug", usage="Show internal variable content", widget="StringArray", metaVar="OFF	ON	ALL	REALLY ALL", required=true, def="OFF")
 		debug
-if (!ParametersDialog.open(this)) return;
+
+@Field @Option(name="outputFile", usage="set outputFile to ' ' (space) to write in console", widget="CreateFile", required=false, def=" ")
+File outputFile
+
+if (!ParametersDialog.open(this)) return
+
 if (debug == "OFF") debug = 0; else if (debug == "ON") debug = 1; else if (debug == "ALL") debug = 2 else if (debug == "REALLY ALL") debug = 3
 
-
 def CQI = CQPSearchEngine.getCqiClient()
 
+if (outputFile != null) {
+	println "Outputing to $outputFile"
+	outStream = new PrintStream(outputFile)
+} else {
+	outStream = System.out
+}
+
 //corpus = corpusViewSelection
 def allResults = [:]
 for (def corpus : selection) {
@@ -96,25 +107,39 @@
 			def form = StringUtils.join(CQI.cpos2Str(word.getQualifiedName(), pos), " ")
 
 			def props = unit.getProps().sort()
-			println sprintf("#%4d, %d-%d, \"%s\" %s", n, unit.getDeb(), unit.getFin(), form, props)
+			outStream << sprintf("#%4d, %d-%d, \"%s\" %s", n, unit.getDeb(), unit.getFin(), form, props)
+			outStream << "\n"
 			n++
 		}
 	}
 } else if (output_mode == "TABULATED") {
+
 	for (def corpus : allResults.keySet()) {
 		def word = corpus.getWordProperty()
 		def selectedUnits = allResults[corpus]
-		println "$corpus units: "+selectedUnits.size()
-		def n = 1
-		for (def unit : selectedUnits) {
-			int[] pos = null
-			if (unit.getDeb() == unit.getFin()) pos = [unit.getDeb()]
-			else pos = (unit.getDeb()..unit.getFin())
-			def form = StringUtils.join(CQI.cpos2Str(word.getQualifiedName(), pos), " ")
-
-			def props = unit.getProps().sort()
-			println sprintf("#%4d\t%d\t%d\t%s\t%s", n, unit.getDeb(), unit.getFin(), form, props)
-			n++
+		
+		println ""+selectedUnits.size()+" units in $corpus.\n"
+		
+		if (selectedUnits.size() > 0) {
+			def n = 1
+		
+			outStream << "#\tstart\tend\tform"
+			selectedUnits[0].getProps().keySet().sort().each { outStream << "\t"+it }
+			outStream << "\n"
+		
+			for (def unit : selectedUnits) {
+				int[] pos = null
+				if (unit.getDeb() == unit.getFin()) pos = [unit.getDeb()]
+				else pos = (unit.getDeb()..unit.getFin())
+				def form = StringUtils.join(CQI.cpos2Str(word.getQualifiedName(), pos), " ")
+				def props = unit.getProps()
+			
+				outStream << sprintf("%d\t%d\t%d\t%s", n, unit.getDeb(), unit.getFin(), form)
+				props.sort()*.value.each { outStream << "\t"+it }
+				outStream << "\n"
+			
+				n++
+			}
 		}
 	}
 } else if (output_mode == "COUNT") {
