Built-in operators and functions
الاضافة +
يقوم هذا المعامل بتطبيق عدة فلاتر ثم جمع النتائج معا
اذا كانت النتائج أرقام: يتم جمع الارقام
اذا كانت النتائج حروف يتم دمجهم الي سلسلة حروف اكبر
اذا كانت النتائج مصفوفات يتم دمجهم في مصفوفة كبيرة
اذا كانت النتائج Objects يتم عمل merge لهم, ولعمل deep merge استخدم المعامل
*اذا كان أحد النتائج null لا يتم تغيير شئ
$ echo '{"a": 7}' | jq '.a + 1'
8
$ echo '{"a": [1,2], "b": [3,4]}' | jq '.a + .b'
[1, 2, 3, 4]
$ echo '{"a": 1}' | jq '.a + null'
1
$ echo '{}' | jq '.a + 1'
1 # null + 1 = 1
$ echo null | jq '{a: 1} + {b: 2} + {c: 3} + {a: 42}'
{
"a": 42,
"b": 2,
"c": 3
}الطرح -
يشبه عملية الاضافة وللكن بالعكس
في حالة المصفوفات يتم حذف العناصر بدلا من اضافتها
$ echo '{"a":3}' | jq '4 - .a'
1
$ echo '["xml", "yaml", "json"]' | jq '. - ["xml", "yaml"]'
[
"json"
]
عمليات الضرب والقسمة
بالنسبة للارقام يتم اجراء العمليات الحسابية العادية
عند ضرب string في رقم يتم تكرار الـ string هذا العدد من المرات
أما قسمة string علي string اخر يؤدي الي تقسيم ال string الاصلي الي أجزاء عند مكان الـ string الاخر
// dividing number
// `10 / 5 * 3` = 6
$ echo 5 | jq '10 / . * 3'
6
$ echo "a, b,c,d, e" | jq '. / ", "'
jq: parse error: Invalid numeric literal at line 1, column 2
[ble: exit 5]
// split strings at ", "
$ echo '"a, b,c,d, e"' | jq '. / ", "'
[
"a",
"b,c,d",
"e"
]
// deep merging objects
// for shallow meging use `+`
$ echo null | jq '{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}'
{
"k": {
"a": 0,
"b": 2,
"c": 3
}
}
// divide numbers
// note: we use `?` here to avoid error when dividing by zero
$ echo [1,0,-1] | jq '.[] | (1 / .)?'
1
-1
$ echo [1,0,-1] | jq '.[] | (1 / .)'
1
jq: error (at :1): number (1) and number (0) cannot be divided because the divisor is zeroالقيمة المطلقة abs
وهو يستخدم لايجاد القيمة المطلقة (الموجبة) للرقم
$ echo 1 | jq abs
1
$ echo -1 | jq abs
1
$ echo 0 | jq abs
0
$ echo -1e-1 | jq abs
0.1الطول length
بالنسبة للـ strings يكون عدد حروف النص
بالنسبة للأرقام يعطي القيمة الموجبة (مثل abs)
بالنسبة للمصفوفات والـ objects يكون عدد عناصر المصفوفة
طول null هو صفر
$ echo -1 | jq length
1
$ echo "hello" | jq length
jq: parse error: Invalid numeric literal at line 2, column 0
[ble: exit 5]
$ echo '"hello"' | jq length
5
$ echo '[1, 2, 3]' | jq length
3
$ echo '{"a": 1, "b": 2}' | jq length
2
$ echo null | jq length
0
$ echo true | jq length
jq: error (at :1): boolean (true) has no lengthبالنسبة لـ UTF8 unicode strings يمكن استخدام الدالة utf8bytelength
$ echo '"😉"' | jq length
1
$ echo '"😉"' | jq utf8bytelength
4
$ echo '"\u03bc"' | jq utf8bytelength
2الدالة keys
تعود هذه الدالة بمفاتيح الـ object مرتبة تصاعديا, وللحصول علي المفاتيح بدون ترتيب يمكن استخدام keys_unsorted
$ echo '{"a": 1, "c": 2, "b": 3}' | jq keys
["a", "b", "c" ]
$ echo '{"a": 1, "c": 2, "b": 3}' | jq keys_unsorted
["a", "c", "b"]
$ echo '[1, 2, 3]' | jq keys
[0, 1, 2]ويمكن معرفة اذا كان العنصر يحتوي علي مفتاح ما باستخدام has(key)
$ echo '{"a": 1, "c": 2, "b": 3}' | jq 'has("a")'
true
$ echo '{"a": 1, "c": 2, "b": 3}' | jq 'has("x")'
falseأو باستحدام in
$ echo '"a"' | jq 'in({"a": 1})'
true
$ echo '"b"' | jq 'in({"a": 1})'
false
$ echo '1' | jq 'in([1, 2, 3])'
true
$ echo '5' | jq 'in([1, 2, 3])'
falseاختيار عناصر باستخدام pick
$ echo '{"a": 1, "b": {"c": 2, "d": 3}, "e": 4}' | jq 'pick(.a, .b.c, .x)'
{
"a": 1,
"b": {
"c": 2
},
"x": null
}
بالنسبة لللمصفوفات يتم اختيار العناصر المحددة وارجاعها بنفس الترتيب
$ echo [1,2,3,4] | jq 'pick(.[0])'
[1]
$ echo [1,2,3,4] | jq 'pick(.[1])'
[null, 2]
$ echo [1,2,3,4] | jq 'pick(.[2])'
[null, null, 3]
$ echo [1,2,3,4] | jq 'pick(.[0, 1])'
[1, 2]// pick(.[2]) -> [null, null, 3]
// pick(.[0]) -> [1]
// all together: [null, null, 3] + [1] + [1] = [1, null, 3]
// this gives the same result as `pick(.[0], .[2], .[0])`
$ echo [1,2,3,4] | jq 'pick(.[2], .[0], .[0])'
[1, null, 3]ويمكن الحصول علي مسار اي عنصر باستخدام path
النتيجة تكون array كل عنصر فيها يحدد segment من المسار
$ echo '{"a": [{"b": 1}]}' | jq 'path(.a)'
["a"]
$ echo '{"a": [{"b": 1}]}' | jq 'path(.a[0])'
["a", 0]
$ echo '{"a": [{"b": 1}]}' | jq 'path(.a[0].b)'
["a", 0, "b"]
$ echo '{"a": [{"b": 1}]}' | jq 'path(.)'
[]
// reach the most deep element using the recursive descent operator `..`
$ echo '{"a": [{"b": 1}]}' | jq 'path(..)'
[]
["a"]
["a", 0]
["a", 0, "b"]لنتدبر قليلا المثال الأخير حتي نفهم العملية بعمق أكبر
المعامل .. يستخدم للوصول الي اعمل عنصر داحل المصفوفات والـ objects وبالتالي path(..) يعني الحصول علي مسار أعمل عنصر في الـ input
وهو يقوم بارجاع مصفوفة كل عنصر فيها يمثل segment من المسار
الـ segment الاول [] هو مسار الـ root
ال segement الثاني هو مسار العنصر “a'“
الـ segment الثالث [“a”, 0] هو مسار العنصر الاول في المصفوفة داخل العنصر “a”
الـ segment الرابع [“a”, 0, “b”] هو مسار العنصر “b” داخل العنصر الاول في المصفوفة داخل العنصر “a”
فكر فيها كأنها شجرة
(root)
└── a
└── 0
└── bويمكن الحصول علي قيمة عنصر عند مسار محدد باستخدام getpath
أو تحديد قيمة عنصر في مسار معين باستخدام setpath(path; value)
ويمكن حذف عنصر عند مسار معين باستخدام delpaths
$ echo '{"report" : {"test": "ok"}}' | jq 'getpath(["report"])'
{"test": "ok"}
$ echo '{"report" : {"test": "ok"}}' | jq 'getpath(["report", "test"])'
"ok"
$ echo '{"a":{"b":0, "c":1}}' | jq '[getpath(["a","b"], ["a","c"])]'
[0, 1]
// set path using `setpath(path; value)`
$ echo '{"report" : {"test": "ok"}}' | jq 'setpath(["report", "test"]; "failed")'
{
"report": {
"test": "failed"
}
}
$ echo '{"report" : {"test": "ok"}}' | jq 'setpath(["report", "test"]; "failed") | getpath(["report", "test"])'
"failed"
$ echo null | jq 'setpath(["a","b"]; 1)'
{
"a": {
"b": 1
}
}
$ echo null | jq 'setpath([0,"a"]; 1)'
[
{
"a": 1
}
]
// delete path
$ echo '{"a":{"b":1},"x":{"y":2}}' | jq 'delpaths([["a","b"]])'
{
"a": {},
"x": {
"y": 2
}
}ويمكن استخدام paths للحصول علي مسارات جميع عناصر الـ input
$ echo '[1,[[],{"a":2}]]' | jq paths
[[0],[1],[1,0],[1,1],[1,1,"a"]]
// paths of number values only
$ echo '[1,[[],{"a":2}], null]' | jq 'paths(type == "number")'
[[0],[1,1,"a"]]التحويل من object الي مصفوفة او العكس
يمكن استخدام الدوال to_entries, from_entries, with_entries لتحويل مصففوفة الي object او العكس
بالنسبة لـ with_entries(filter) فهي اهتصار لـ to_entries | map(filter) | from_entries أي تحويل الـ object الي مصفوفة من key-value pairs وتطبيق الفلتر علي كل عنصر من عناصرها ثم اعادتها لـ object جديد
$ echo '{"a": 1, "b": 2}' | jq to_entries
[
{
"key": "a",
"value": 1
},
{
"key": "b",
"value": 2
}
]
$ echo '[{"key":"a", "value":1}, {"key":"b", "value":2}]' | jq from_entries
{
"a": 1,
"b": 2
}
$ echo '{"a": 1, "b": 2}' | jq 'with_entries(.key |= "KEY_" + .)'
{
"KEY_a": 1,
"KEY_b": 2
}
لنأخذ المثال الأخير هطوة خطوة, كما ذكرنا فان with_entries تقوم بتحويل الـ object الي مصفوفة ثم تطبيق الفلتر علي كل عنصر من عناصر هذه المصفوفة ثم اعادتها لـ object مرة اخري
الفلتر المستخدم هنا هو .key |= "KEY_" + . وهو يعني:
.key→ مفتاح العنصر الحالي|=→ modify”KEY_” + .→ add KEY_ to the current value
// step 1: apply `to_entries`
$ echo '{"a": 1, "b": 2}' | jq to_entries
[
{"key": "a", "value": 1 },
{"key": "b", "value": 2}
]
// step 2: apply the filter to each item of the array (without the modification part)
$ echo '[{"key": "a", "value": 1 }, {"key": "b", "value": 2}]' | jq 'map(.key | .)'
["a", "b"]
// let's add another part to the filter `|=`
$ echo '[{"key": "a", "value": 1 }, {"key": "b", "value": 2}]' | jq 'map(.key |= .)'
[
{
"key": "a",
"value": 1
},
{
"key": "b",
"value": 2
}
]
// now let's see the full filter effect
$ echo '[{"key": "a", "value": 1 }, {"key": "b", "value": 2}]' | jq 'map(.key |= "KEY_" + .)'
[
{"key": "KEY_a", "value": 1},
{"key": "KEY_b", "value": 2}
]
// step 3: convert the array back into an object using `from_entries`
$ echo '[{"key": "KEY_a", "value": 1}, {"key": "KEY_b", "value": 2}]' | jq from_entries
{
"KEY_a": 1,
"KEY_b": 2
}
القيمة الفارغة
يمكن ارجاع قيمة فارغة تماما (ولا حتي null) باستخدام empty
$ echo null | jq 'empty'
$ echo null | jq '1, empty, 2'
1
2ويمكن استخدام هذه الميزة مثلا في الفلترة (بديل لـ select)
لنستعير أحد الأمثلة التي استخدمنا فيها select ونستخدم empty بدلا منها
$ echo '[1,5,3,0,7]' | jq '.[] | select(. > 2)'
5
3
7
$ echo '[1,5,3,0,7]' | jq '.[] | if . > 2 then . else empty end'
5
3
7
// remove elements from an array (using `map()` or `[...]`)
$ echo '[1,5,3,0,7]' | jq 'map(if . > 2 then . else empty end)'
[5, 3, 7]
// remove null values
$ echo '[1,null,2]' | jq '.[] | if . == null then empty else . end'
$ echo null | jq '. // empty'
$ echo true | jq '. // empty'
true
المعامل // يعني القيمة الافتراضية اذا كانت القيمة الاصلية falsy ويمكن الاستفادة من هذه الحركة في حذف العناصر التي تعطي قيمة falsy
$ echo '[1, true, false, null, "ok"]' | jq 'map(. // empty)'
[1, true, "ok"]
// to demonstrate the operator `//` better:
$ echo '[1, true, false, null, "ok"]' | jq 'map(. // "falsy value")'
[
1,
true,
"falsy value",
"falsy value",
"ok"
]التعامل مع الاخطاء
يمكن رمي Exception باستخدام الدالة error ويمكن تمرير رسالة اليها error(message)
الرسالة الافتراضية هي قيمة العنصر الحالي
ويمكن مسك الخطأ باستخدام try & catch
وللحصول علي اسم الملف ورقم السطر الذي حدث فيه الايرور يمكن استخدام $__loc__
$ echo '"something wrong"' | jq 'error'
jq: error (at :1): something wrong
$ echo '"something wrong"' | jq 'error("custom message")'
jq: error (at :1): custom message
$ echo '"something wrong"' | jq 'try error("custom message") catch "oops"'
"oops"
$ echo '"something wrong"' | jq 'try error("custom message") catch .'
"custom message"
$ echo '"something wrong"' | jq 'try error catch .'
"something wrong"
$ echo null | jq 'try error("\($__loc__)") catch .'
"{\"file\":\"\",\"line\":1}"
ويمكن ايقاف jq بدون أي نتائج مع exit code 0 باستخدام halt او مع خطأ باستخدام halt_error أو halt_error(exit_code = 5)
// exit code: 0
$ echo null | jq 'halt'
// exit code: 5 (the default)
$ echo null | jq 'halt_error'
exit 5
// exit code: 1
$ echo null | jq 'halt_error(1)'
exit 1التعامل مع الارقام
يمكن توليد أرقام باستخدام range
// from 0 up to 3
$ echo null | jq 'range(3)'
0
1
2
// from 3 up to 5
$ echo null | jq 'range(3; 5)'
3
4
// from 1 up to 10, every 2 steps
$ echo null | jq 'range(1; 10; 2)'
1
3
5
7
9
// inverse counting
$ echo null | jq 'range(1; -5; -1)'
1
0
-1
-2
-3
-4يتم تقريب الارقام باستخدام floor
$ echo 3.14159 | jq floor
3ولحساب الجزر التربيعي لرقم استخدم sqrt
$ echo 9 | jq sqrt
3تحويل الأنواع
يمكن استخدام دوال to* مثل tonumber و tostring لتحويل الأنواع
ويمكن التحقق من نوع عنصر باستخدام الدوال is* مثل isinfinite
وكذلك يمكن استخدام الدالة type للحصول علي نوع العنصر
$ echo '"1"' | jq tonumber
1
$ echo '"test"' | jq type
"string"
$ echo 1 | jq isfinite
trueتطبيق فلتر بشكل متكرر علي المدخلات
يمكن استخدام الدالة while لتطبيق فلتر معين بشكل متكرر, هذه الدالة تأخذ شرط, ويتم تكرار الفلتر حتي يصبح الشرط false
مثلا لأخذ رقم وضربه في 2 طالما الناتج أقل من 100
$ echo 1 | jq '[while(.<100; .*2)]'
[1,2,4,8,16,32,64]أو باستخدام until حيث يتم التكرار حتي يتحقق الشرط
$ echo 1 | jq '[until(.>100; .*2)]' -c
[128]أو يمكن استخدام repeate للتكرار حتي حدوث خطأ
$ echo 1 | jq '[repeat(.*2, error)?]'
[2]ويمكن استخدام recurse لاستخراج داتا من كل المستويات
مثلا لو لدينا نظام ملفات بهذا الشكل
{
"name": "/",
"children": [
{
"name": "/bin",
"children": [
{
"name": "/bin/ls",
"children": []
},
{
"name": "/bin/sh",
"children": []
}
]
},
{
"name": "/home",
"children": [
{
"name": "/home/stephen",
"children": [
{
"name": "/home/stephen/jq",
"children": []
}
]
}
]
}
]
}ونريد استخراج أسماء جميع الملفات, في العادي سنحتاج لعمل شئ مثل:
.name
.children[].name
.children[].children[].name
...أو يمكن ببساطة استخدام recurse لهذا الغرض
$ echo '{
"name": "/",
"children": [
{
"name": "/bin",
"children": [
{
"name": "/bin/ls",
"children": []
},
{
"name": "/bin/sh",
"children": []
}
]
},
{
"name": "/home",
"children": [
{
"name": "/home/stephen",
"children": [
{
"name": "/home/stephen/jq",
"children": []
}
]
}
]
}
]
}' | jq 'recurse(.children[]) | .name'
"/"
"/bin"
"/bin/ls"
"/bin/sh"
"/home"
"/home/stephen"
"/home/stephen/jq"قمنا أولا بتطبيق الفلتر recurse(.children[]) وهو يقوم باستخراج children لجميع المستويات, ثم تطبيق فلتر .name علي كل نتيجة لاستخراج اسم الملف
ويمكن تمرير شرط لـ recurse() لتنفيذ العملية طالما ان الشرط متحقق
echo 1 | jq 'recurse(. *2; . < 5)'
1
2
4مثال اخر لاستخراج كل العناصر في جميع المستويات
$ echo '{"a":0,"b":[1]}' | jq recurse
{"a": 0, "b": [1]} //<-- level 1: the object itself
0 //<-- first item of level 2: `"a": 0`
[1] //<-- second item of level 2: `"b": [1]`
1 //<-- level 3أما الدالة walk تقوم بتطبيق فلتر معين علي كل العناصر, وفي حالة وجود عنصر عبارة عن مصفوفة تقوم أولا بتطبيق الفلتر علي كل عناصر المصفوفة ثم علي المثفوفة نفسها, وفي حالة Object يتم تطبيق الفلتر علي كل الـ values ثم علي الـ object نفسه
مثلا لترتيب عناصر كل مصفوفة:
$ echo '[[4, 1, 7], [8, 5, 2], [3, 6, 9]]' | jq 'walk(if type == "array" then sort else . end)'
[[1,4,7],[2,5,8],[3,6,9]]في المثال السابق, تم استخدام walk لتطبيق الفلتر علي كل عناصر كل مصفوففة ثم علي المصفوفة نفسها
الفلتر نفسه يقول, اذا كان العنصر Array قم بتطبيق sork والذي يقوم بترتيب عناصر المصفوفةو غير ذلك قم بتطبيق . أي اترك العنصر كما هو.
مثال اخر علي حالة object, وفي هذا المثال نريد ازالة “_” في بداية اسم كل مفتاح, هيا نفكر سويا في الحل
بفرض أن لدينا object كالتالي ونريد حذف “_” من بداية اسم أي مفتاح في هذا الـ object
[ { "_a": { "__b": 2 } } ]طبعا سيتم حذف هذا الرمز باستخدام sub والذي يقوم باستبدال حروف باخري, ولاننا نريد “_” في بداية الكلمة, اذا سنستخدم هذا ال RegExp ^_+ وهو يعني:
^→ at the beginning+any number of it
لتطبيق هذا الفلتر علي عناصر object سنستخدم with_entries فنقول
with_entries(.key |= sub("^_+"; ""))استخدمنا .key للحصول علي مفاتيح الـ object, والعلامة |= تعني اجراء تعديل عليه
الان نريد تطبيق ذلك علي جميع الـ objects والـ objects الفرعية في جميع المستويات فنستخدم walk وتكون النتيجة النهائية:
$ echo '[ { "_a": { "__b": 2 } } ]' | jq 'walk( if type == "object" then with_entries( .key |= sub( "^_+"; "") ) else . end )'
[{"a":{"b":2}}]