multilang.sh
diff --git a/examples/js/cli.js b/examples/js/cli.js
index d326d86a98be..a20eba700f53 100644
--- a/examples/js/cli.js
+++ b/examples/js/cli.js
@@ -146,8 +146,7 @@ function printUsage () {
 //-----------------------------------------------------------------------------
 
 const printHumanReadable = (exchange, result) => {
-
-    if (Array.isArray (result) || table) {
+    if (!no_table && Array.isArray (result) || table) {
         result = Object.values (result)
         let arrayOfObjects = (typeof result[0] === 'object')
 
@@ -158,33 +157,32 @@ const printHumanReadable = (exchange, result) => {
                 log (object)
             })
 
-        if (!no_table)
-            if (arrayOfObjects || table && Array.isArray (result)) {
-                log (result.length > 0 ? asTable (result.map (element => {
-                    let keys = Object.keys (element)
-                    delete element['info']
-                    keys.forEach (key => {
-                        if (!iso8601)
-                            return element[key]
-                        try {
-                            const iso8601 = exchange.iso8601 (element[key])
-                            if (iso8601.match (/^20[0-9]{2}[-]?/))
-                                element[key] = iso8601
-                            else
-                                throw new Error ('wrong date')
-                        } catch (e) {
-                            return element[key]
-                        }
-                    })
-                    return element
-                })) : result)
-                log (result.length, 'objects');
-            } else {
-                console.dir (result, { depth: null })
-                log (result.length, 'objects');
-            }
+        if (arrayOfObjects || table && Array.isArray (result)) {
+            log (result.length > 0 ? asTable (result.map (element => {
+                let keys = Object.keys (element)
+                delete element['info']
+                keys.forEach (key => {
+                    if (!iso8601)
+                        return element[key]
+                    try {
+                        const iso8601 = exchange.iso8601 (element[key])
+                        if (iso8601.match (/^20[0-9]{2}[-]?/))
+                            element[key] = iso8601
+                        else
+                            throw new Error ('wrong date')
+                    } catch (e) {
+                        return element[key]
+                    }
+                })
+                return element
+            })) : result)
+            log (result.length, 'objects');
+        } else {
+            console.dir (result, { depth: null })
+            log (result.length, 'objects');
+        }
     } else {
-        console.dir (result, { depth: null})
+        console.dir (result, { depth: null, maxArrayLength: null })
     }
 }
 
diff --git a/examples/py/cli.py b/examples/py/cli.py
index 9dd972cd6c81..b18de00ef49e 100644
--- a/examples/py/cli.py
+++ b/examples/py/cli.py
@@ -168,6 +168,7 @@ def print_usage():
     method = getattr(exchange, argv.method)
     # if it is a method, call it
     if callable(method):
+        print(f"{argv.exchange_id}.{argv.method}({','.join(args)})")
         result = method(*args)
     else:  # otherwise it's a property, print it
         result = method
diff --git a/multilang.sh b/multilang.sh
new file mode 100755
index 000000000000..3785e2515ccb
--- /dev/null
+++ b/multilang.sh
@@ -0,0 +1,97 @@
+#!/usr/bin/env bash
+
+function usage() {
+    echo "usage: $0 [-clsh] exchange method [...args]"
+    echo "	-c      Number of lines to trim off the top and bottom of output"
+    echo "	-l      View in less editor"
+    echo "	-s      Remove special characters"
+    echo "	-h      Display help"
+    exit 1
+}
+
+if [[ $# < 1 ]]; then
+  usage
+fi
+
+
+cliFolder='./examples'
+
+jsCli="${cliFolder}/js/cli.js"
+pythonCli="${cliFolder}/py/cli.py"
+phpCli="${cliFolder}/php/cli.php"
+
+useLess=false
+removeSpecial=false
+numLines=0
+
+function display () {
+    # Displays output in a less window or just to stdout
+    if ${useLess}; then
+        less -S -R
+    else
+        tee
+    fi
+}
+
+function removeSpecial {
+    # Removes special characters
+    if ${removeSpecial}; then
+        sed -e 's/\[[0-9]\{1,2\}m//g'
+    else
+        tee
+    fi
+}
+
+function condense {
+    # Trims output down to a set number of lines on the top and the bottom
+    if [ ${numLines} -gt 0 ]; then
+        awk 'NF' | awk -v head=${numLines} -v tail=${numLines} 'FNR<=head
+            {lines[FNR]=$0}
+            END{
+                for (i=FNR-tail+1; i<=FNR; i++) print lines[i]
+            }'
+    else
+        tee
+    fi
+}
+
+function removeAndColorLines {
+  sed -E -e '/.*(iteration|Array|^202.*|^$)/d' -e 's/  / /g' -e "s/(.*)/$(tput setaf $color)\1$(tput sgr0)/"
+}
+
+function writeOutput() {
+  local interpretter="$1"
+  local path="$2"
+  local args="$3"
+  $interpretter "$path" $args | removeSpecial | removeAndColorLines $color
+}
+
+# Loop through command line arguments
+while getopts 'hc:sl' flag; do
+    case "${flag}" in
+        h) usage ;;
+        c) numLines="${OPTARG}" ;;
+        s) removeSpecial=true ;;
+        l) useLess=true ;;
+        *) usage ;;
+    esac
+done
+
+shift $((OPTIND-1))
+
+args="$@"
+
+color=3
+jsOutput=$(writeOutput node $jsCli "--no-table $args")
+((color++))
+
+pythonOutput=$(writeOutput python3 $pythonCli "$args")
+((color++))
+
+# python has the shortest output
+length=$(wc -l <<< "$pythonOutput")
+
+phpOutput=$(writeOutput php $phpCli "$args")
+((color++))
+
+paste <(echo "$jsOutput") <(echo "$phpOutput") <(echo "$pythonOutput") | column -s $'\t' -t | head -n $length | condense | display

There's a few things that aren't perfect yet, but I think we should start using something like this to test all the languages at once
- [x] only works in bash because of process substitution, and not sh currently
- [x] would like to get rid of the `-a` argument, and just accept the rest of the arguments that aren't next to a letter
- [ ] would make the node, php, and python output match more exactly line by line (but that wouldn't be a part of this script)

for methods that return tables, 
- [x] python and php dont display a table
- [x] `column: line too long` error with `fetchMarkets`

-----------

```
% bash test.bash -a "binance fetchTicker XRP/USDT"
2022-03-12T02:12:42.951Z                               PHP v8.1.3                                       Python v3.9.5
Node.js: v14.17.0                                      CCXT v1.75.87                                    CCXT v1.75.87
CCXT v1.75.87                                          binance->fetchTicker(XRP/USDT)                   {'ask': 0.8397,
binance.fetchTicker (XRP/USDT)                         Array                                             'askVolume': 11016.0,
2022-03-12T02:12:44.659Z iteration 0 passed in 641 ms  (                                                 'average': 0.7841,
    [symbol] => XRP/USDT                                'baseVolume': 652472355.0,
{                                                          [timestamp] => 1647051167504                  'bid': 0.8396,
  symbol: 'XRP/USDT',                                      [datetime] => 2022-03-12T02:12:47.504Z        'bidVolume': 10160.0,
  timestamp: 1647051164295,                                [high] => 0.849                               'change': 0.1112,
  datetime: '2022-03-12T02:12:44.295Z',                    [low] => 0.725                                'close': 0.8397,
  high: 0.849,                                             [bid] => 0.8399                               'datetime': '2022-03-12T02:12:45.891Z',
  low: 0.725,                                              [bidVolume] => 21263                          'high': 0.849,
  bid: 0.8396,                                             [ask] => 0.84                                 'info': {'askPrice': '0.83970000',
  bidVolume: 12076,                                        [askVolume] => 1598                                    'askQty': '11016.00000000',
  ask: 0.8397,                                             [vwap] => 0.77904089                                   'bidPrice': '0.83960000',
  askVolume: 9047,                                         [open] => 0.7285                                       'bidQty': '10160.00000000',
  vwap: 0.77903801,                                        [close] => 0.8399                                      'closeTime': '1647051165891',
  open: 0.7285,                                            [last] => 0.8399                                       'count': '660132',
  close: 0.8396,                                           [previousClose] => 0.72850000                          'firstId': '428019321',
  last: 0.8396,                                            [change] => 0.1114                                     'highPrice': '0.84900000',
  previousClose: '0.72850000',                             [percentage] => 15.292                                 'lastId': '428679452',
  change: 0.1111,                                          [average] => 0.7842                                    'lastPrice': '0.83970000',
  percentage: 15.251,                                      [baseVolume] => 652493015                              'lastQty': '16.00000000',
  average: 0.78405,                                        [quoteVolume] => 508318740.7644                        'lowPrice': '0.72500000',
  baseVolume: 652475266,                                   [info] => Array                                        'openPrice': '0.72850000',
  quoteVolume: 508303035.8246,                                 (                                                  'openTime': '1646964765891',
  info: {                                                          [symbol] => XRPUSDT                            'prevClosePrice': '0.72860000',
    symbol: 'XRPUSDT',                                             [priceChange] => 0.11140000                    'priceChange': '0.11120000',
    priceChange: '0.11110000',                                     [priceChangePercent] => 15.292                 'priceChangePercent': '15.264',
    priceChangePercent: '15.251',                                  [weightedAvgPrice] => 0.77904089               'quoteVolume': '508301025.68420000',
    weightedAvgPrice: '0.77903801',                                [prevClosePrice] => 0.72850000                 'symbol': 'XRPUSDT',
    prevClosePrice: '0.72850000',                                  [lastPrice] => 0.83990000                      'volume': '652472355.00000000',
    lastPrice: '0.83960000',                                       [lastQty] => 6428.00000000                     'weightedAvgPrice': '0.77903841'},
    lastQty: '51.00000000',                                        [bidPrice] => 0.83990000              'last': 0.8397,
    bidPrice: '0.83960000',                                        [bidQty] => 21263.00000000            'low': 0.725,
    bidQty: '12076.00000000',                                      [askPrice] => 0.84000000              'open': 0.7285,
    askPrice: '0.83970000',                                        [askQty] => 1598.00000000             'percentage': 15.264,
    askQty: '9047.00000000',                                       [openPrice] => 0.72850000             'previousClose': '0.72860000',
    openPrice: '0.72850000',                                       [highPrice] => 0.84900000             'quoteVolume': 508301025.6842,
    highPrice: '0.84900000',                                       [lowPrice] => 0.72500000              'symbol': 'XRP/USDT',
    lowPrice: '0.72500000',                                        [volume] => 652493015.00000000        'timestamp': 1647051165891,
    volume: '652475266.00000000',                                  [quoteVolume] => 508318740.76440000   'vwap': 0.77903841}
    quoteVolume: '508303035.82460000',                             [openTime] => 1646964767504
    openTime: '1646964764295',                                     [closeTime] => 1647051167504
    closeTime: '1647051164295',                                    [firstId] => 428019326
    firstId: '428019313',                                          [lastId] => 428679470
    lastId: '428679448',                                           [count] => 660145
    count: '660136'                                            )
  }
}                                                      )
```