#!/usr/bin/env bash # # Jsawk: It's like awk for JSON, in bash. # # Fork me on github: # http://github.com/micha/jsawk # # Author: # Micha Niskin # Copyright 2009, no rights reserved, other than as required by the # licenses of the incorporated software below. # TMP1=`mktemp /tmp/tmp.XXXXXX` TMP2=`mktemp /tmp/tmp.XXXXXX` trap "rm -f $TMP1 $TMP2" SIGINT SIGTERM SIGHUP SIGQUIT cat <<'__END__' > $TMP1 window = this; // the global object window.IS = []; // the input set window.RS = []; // the result set window.$_ = {}; // the current element index window.$$ = {}; // the current element // Underscore.js 1.3.1 // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. // Underscore is freely distributable under the MIT license. // Portions of Underscore are inspired or borrowed from Prototype, // Oliver Steele's Functional, and John Resig's Micro-Templating. // For all details and documentation: // http://documentcloud.github.com/underscore (function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c, h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each= b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a== null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect= function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e= e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a, c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}}; b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments, 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)}; b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"}; b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a), function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+ u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]= function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain= true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); (function() { /* Copyright Jason E. Smith 2008 Licensed under the Apache License, Version 2.0 (the "License"); You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 */ /* * CREDITS: * Thanks to Kris Zyp from SitePen for contributing his source for * a standalone port of JSONQuery (from the dojox.json.query module). * * OVERVIEW: * JSONQuery.js is a standalone port of the dojox.json.query module. It is intended as * a dropin solution with zero dependencies. JSONQuery is intended to succeed and improve upon * the JSONPath api (http://goessner.net/articles/JsonPath/) which offers rich powerful * querying capabilities similar to those of XQuery. * * EXAMPLES / USAGE: * see http://www.sitepen.com/blog/2008/07/16/jsonquery-data-querying-beyond-jsonpath/ * * *Ripped from original source. * JSONQuery(queryString,object) and JSONQuery(queryString)(object) always return identical results. The first one immediately evaluates, the second one returns a function that then evaluates the object. example: JSONQuery("foo",{foo:"bar"}) This will return "bar". example: evaluator = JSONQuery("?foo='bar'&rating>3"); This creates a function that finds all the objects in an array with a property foo that is equals to "bar" and with a rating property with a value greater than 3. evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}]) This returns: {foo:"bar",rating:4} example: evaluator = JSONQuery("$[?price<15.00][\rating][0:10]"); This finds objects in array with a price less than 15.00 and sorts then by rating, highest rated first, and returns the first ten items in from this filtered and sorted list. example: var data = {customers:[ {name:"Susan", purchases:29}, {name:"Kim", purchases:150}, {name:"Jake", purchases:27} ]}; var results = json.JSONQuery("$.customers[?purchases > 21 & name='Jake'][\\purchases]",data); results returns customers sorted by higest number of purchases to lowest. */ function map(arr, fun /*, thisp*/){ var len = arr.length; if (typeof fun != "function") throw new TypeError(); var res = new Array(len); var thisp = arguments[2]; for (var i = 0; i < len; i++) { if (i in arr) res[i] = fun.call(thisp, arr[i], i, arr); } return res; } function filter(arr, fun /*, thisp*/){ var len = arr.length; if (typeof fun != "function") throw new TypeError(); var res = new Array(); var thisp = arguments[2]; for (var i = 0; i < len; i++) { if (i in arr) { var val = arr[i]; // in case fun mutates this if (fun.call(thisp, val, i, arr)) res.push(val); } } return res; }; function slice(obj,start,end,step){ // handles slice operations: [3:6:2] var len=obj.length,results = []; end = end || len; start = (start < 0) ? Math.max(0,start+len) : Math.min(len,start); end = (end < 0) ? Math.max(0,end+len) : Math.min(len,end); for(var i=start; i, <=, >=, != - These operators behave just as they do // in JavaScript. // // // // | dojox.json.query(queryString,object) // and // | dojox.json.query(queryString)(object) // always return identical results. The first one immediately evaluates, the second one returns a // function that then evaluates the object. // // example: // | dojox.json.query("foo",{foo:"bar"}) // This will return "bar". // // example: // | evaluator = dojox.json.query("?foo='bar'&rating>3"); // This creates a function that finds all the objects in an array with a property // foo that is equals to "bar" and with a rating property with a value greater // than 3. // | evaluator([{foo:"bar",rating:4},{foo:"baz",rating:2}]) // This returns: // | {foo:"bar",rating:4} // // example: // | evaluator = dojox.json.query("$[?price<15.00][\rating][0:10]"); // This finds objects in array with a price less than 15.00 and sorts then // by rating, highest rated first, and returns the first ten items in from this // filtered and sorted list. tokens = []; var depth = 0; var str = []; query = query.replace(/"(\\.|[^"\\])*"|'(\\.|[^'\\])*'|[\[\]]/g,function(t){ depth += t == '[' ? 1 : t == ']' ? -1 : 0; // keep track of bracket depth return (t == ']' && depth > 0) ? '`]' : // we mark all the inner brackets as skippable (t.charAt(0) == '"' || t.charAt(0) == "'") ? "`" + (str.push(t) - 1) :// and replace all the strings t; }); var prefix = ''; function call(name){ // creates a function call and puts the expression so far in a parameter for a call prefix = name + "(" + prefix; } function makeRegex(t,a,b,c,d){ // creates a regular expression matcher for when wildcards and ignore case is used return str[d].match(/[\*\?]/) || c == '~' ? "/^" + str[d].substring(1,str[d].length-1).replace(/\\([btnfr\\"'])|([^\w\*\?])/g,"\\$1$2").replace(/([\*\?])/g,".$1") + (c == '~' ? '$/i' : '$/') + ".test(" + a + ")" : t; } query.replace(/(\]|\)|push|pop|shift|splice|sort|reverse)\s*\(/,function(){ throw new Error("Unsafe function call"); }); query = query.replace(/([^=]=)([^=])/g,"$1=$2"). // change the equals to comparisons replace(/@|(\.\s*)?[a-zA-Z\$_]+(\s*:)?/g,function(t){ return t.charAt(0) == '.' ? t : // leave .prop alone t == '@' ? "$obj" :// the reference to the current object (t.match(/:|^(\$|Math|true|false|null)$/) ? "" : "$obj.") + t; // plain names should be properties of root... unless they are a label in object initializer }). replace(/\.?\.?\[(`\]|[^\]])*\]|\?.*|\.\.([\w\$_]+)|\.\*/g,function(t,a,b){ var oper = t.match(/^\.?\.?(\[\s*\^?\?|\^?\?|\[\s*==)(.*?)\]?$/); // [?expr] and ?expr and [=expr and =expr if(oper){ var prefix = ''; if(t.match(/^\./)){ // recursive object search call("expand"); prefix = ",true)"; } call(oper[1].match(/\=/) ? "map" : oper[1].match(/\^/) ? "distinctFilter" : "filter"); return prefix + ",function($obj){return " + oper[2] + "})"; } oper = t.match(/^\[\s*([\/\\].*)\]/); // [/sortexpr,\sortexpr] if(oper){ // make a copy of the array and then sort it using the sorting expression return ".concat().sort(function(a,b){" + oper[1].replace(/\s*,?\s*([\/\\])\s*([^,\\\/]+)/g,function(t,a,b){ return "var av= " + b.replace(/\$obj/,"a") + ",bv= " + b.replace(/\$obj/,"b") + // FIXME: Should check to make sure the $obj token isn't followed by characters ";if(av>bv||bv==null){return " + (a== "/" ? 1 : -1) +";}\n" + "if(bv>av||av==null){return " + (a== "/" ? -1 : 1) +";}\n"; }) + "})"; } oper = t.match(/^\[(-?[0-9]*):(-?[0-9]*):?(-?[0-9]*)\]/); // slice [0:3] if(oper){ call("slice"); return "," + (oper[1] || 0) + "," + (oper[2] || 0) + "," + (oper[3] || 1) + ")"; } if(t.match(/^\.\.|\.\*|\[\s*\*\s*\]|,/)){ // ..prop and [*] call("expand"); return (t.charAt(1) == '.' ? ",'" + b + "'" : // ..prop t.match(/,/) ? "," + t : // [prop1,prop2] "") + ")"; // [*] } return t; }). replace(/(\$obj\s*(\.\s*[\w_$]+\s*)*)(==|~)\s*`([0-9]+)/g,makeRegex). // create regex matching replace(/`([0-9]+)\s*(==|~)\s*(\$obj(\s*\.\s*[\w_$]+)*)/g,function(t,a,b,c,d){ // and do it for reverse = return makeRegex(t,c,d,b,a); }); query = prefix + (query.charAt(0) == '$' ? "" : "$") + query.replace(/`([0-9]+|\])/g,function(t,a){ //restore the strings return a == ']' ? ']' : str[a]; }); // create a function within this scope (so it can use expand and slice) var executor = eval("1&&function($,$1,$2,$3,$4,$5,$6,$7,$8,$9){var $obj=$;return " + query + "}"); for(var i = 0;i 0) { args = args.length > 1 ? args : args[0]; var ret = typeof(args) == "string" ? args : json(args); doPrint(type, ret); } }; window.Q = function() { try { var ret = JSONQuery.apply(window, arguments); ret.length; return ret; } catch (e) { err("jsawk: JSONQuery parse error: '"+arguments[0]+"'"); quit(4); } }; window.out = function() { var args = Array.prototype.slice.call(arguments); args.unshift("OUT:"); doJsonPrint.apply(window, args); }; window.err = function() { var args = Array.prototype.slice.call(arguments); args.unshift("ERR:"); doJsonPrint.apply(window, args); }; window.alert = p; window.doJson = function(input) { if (typeof input !== "string") { return input; } else { input = input.replace(/\s*$/,""); if (!input.length) { return {}; } else { try { return eval("("+input+")"); } catch (e) { err("jsawk: JSON parse error: '"+input+"'"); quit(2); } } } }; window.doCall = function(fun, obj) { try { return fun.call(obj); } catch (e) { err("jsawk: js error: "+e); quit(3); } }; window.makeFilter = function(fun) { try { return eval("(function() { "+fun+"; return this })"); } catch (e) { err("jsawk: script parse error: '"+fun+"'"); quit(3); } }; window.json = function() { try { return JSON.stringify.apply(window, arguments); } catch (e) { err("jsawk: JSON stringify error: "+e); quit(5); } }; window.get = function() { return $$ = IS[++$_]; }; window.put = function(record) { IS = IS.slice(0, $_+1).concat([record]).concat(IS.slice($_+1)); }; window.forEach = function(ary, fun) { fun = eval("function(index,item) { "+fun+"; }"); for (var i=0; i/dev/null)" > $TMP2 nlines=$(grep -c '$' $TMP2 2>/dev/null || echo 0) fi res=$(cat $TMP2 2>/dev/null | js $TMP1 $nlines "$@") ret=$? out=$(echo "$res" |sed '/^OUT: /s/^.....//p;d') err=$(echo "$res" |sed '/^ERR: /s/^.....//p;d') [ -n "$err" ] && echo "$err" 1>&2 [ -n "$out" ] && echo "$out" rm -f $TMP1 $TMP2 exit $ret