আমরা ধরে নিচ্ছি আপনি JSON সিনট্যাক্স ও ডেটা টাইপের সাথে পরিচিত।
jq আগত JSON ডেটাকে একটি একক এক্সপ্রেশন-এর (যা ফিল্টারগুলোর একটি পাইপলাইনে লেখা) মধ্য দিয়ে পাঠিয়ে কাঙ্ক্ষিত রূপান্তরিত ডেটা তৈরি করে।
jq ভাষাটি jq প্রোগ্রাম দিয়ে বাস্তবায়িত হয়েছে।
এই প্রোগ্রামটি বেশ কিছু সুবিধাজনক কমান্ড-লাইন অপশন দেয়, যেগুলো দিয়ে ইনপুট কীভাবে পড়া হবে আর আউটপুট কীভাবে দেখানো হবে তা নিয়ন্ত্রণ করা যায়।
নিচের উদাহরণগুলোতে আপনি পাবেন:
-n অথবা --null-input
সাধারণত jq প্রোগ্রামকে পড়ার জন্য একটি ফাইল দেওয়া হয়, অথবা আপনি তার ইনপুটে ডেটা পাঠান।
--null-input অপশনটি দিয়ে কোনো ইনপুট ছাড়াই JSON ডেটা তৈরি করা যায়।
-c অথবা --compact-output
jq ডিফল্টভাবে তার আউটপুট সুন্দরভাবে সাজিয়ে দেখায়।
ডেটা সুন্দরভাবে সাজানো থাকলে মানুষ তার দিকে তাকাতে দারুণ সুবিধা পায়।
কিন্তু মেশিনের জন্য তা দরকার নেই: --compact-output অপশনটি ফরম্যাটিংয়ের হোয়াইটস্পেস সরিয়ে দিয়ে ফলাফল JSON-এর আকার কমিয়ে আনে।
-f filename অথবা --from-file filename
কমান্ড লাইনে jq প্রোগ্রামটি না দিয়ে বরং filename থেকে তা পড়ুন।
sed আর awk দুটোই একই উদ্দেশ্যে -f অপশন ব্যবহার করে।
প্র্যাকটিস অনুশীলনীগুলোর টেস্ট স্ক্রিপ্টে এটি ব্যবহৃত হতে দেখবেন।
এই পাঠের বাকি অংশে আমরা jq ভাষা নিয়েই আলোচনা করব।
ফিল্টারকে এক্সপ্রেশনও বলা হয়।
একটি ফিল্টার একটি ইনপুট নেয় আর একটি আউটপুট তৈরি করে।
ইউনিক্স শেল-এ যেভাবে কাজ করেন, ঠিক সেভাবেই আপনি পাইপ | দিয়ে _ফিল্টার_গুলো জুড়ে দিতে পারেন, যাতে এক ফিল্টারের আউটপুট আরেকটির ইনপুটে যুক্ত হয়।
.
এটি সবচেয়ে সহজ ফিল্টার। এটি কেবল তার ইনপুটকে আউটপুটে পাঠিয়ে দেয়।
$ echo '[1, 2, 3]' | jq '.'
[
1,
2,
3
]
এটি অ্যারে নিয়ে কাজ করার একটি সংক্ষিপ্ত পরিচিতি হবে। এই বিষয়ে বিস্তারিত আলোচনা পরে করব।
অ্যারে-র এলিমেন্ট অ্যাক্সেস করা হয় বন্ধনী দিয়ে, আর ইনডেক্স শুরু হয় শূন্য থেকে।
$ echo '[10, 20, 30]' | jq '.[1]'
20
একটি ফিল্টার একটি এক্সপ্রেশনকে [ আর ] দিয়ে মুড়ে অ্যারে তৈরি করতে পারে
এলিমেন্টের একটি জানা তালিকা দিয়ে:
jq -n '[1, 2, 3]'
এলিমেন্টের একটি স্ট্রিম সংগ্রহ করতে: যেমন,
range একটি ফাংশন, যা সংখ্যার একটি স্ট্রিম আউটপুট দেয়
$ jq -n 'range(10; 70; 15)'
10
25
40
55
[] ব্যবহার করলে এক্সপ্রেশনের ফলাফলগুলো একটি অ্যারেতে জমা হয়
$ jq -c -n '[range(10; 70; 15)]'
[10,25,40,55]
কমা কেবল অ্যারে-র এলিমেন্ট আলাদা করার সিনট্যাক্স নয়। কমা একটি অপারেটর, যা স্ট্রিমগুলো জুড়ে দেয়।
যেমন [1, 2, 3] হলো একটি ফিল্টার, যা [] অ্যারে কনস্ট্রাক্টর দিয়ে 1, 2 আর 3 এই তিনটি এক্সপ্রেশন জোড়া লাগানোর ফল একত্র করে।
উপরের range(10; 70; 15)-এ সেমিকোলনগুলো খেয়াল করেছেন?
jq ভাষায় কমা-র একটি নির্দিষ্ট কাজ থাকায়, একাধিক আর্গুমেন্ট নেওয়া ফাংশনগুলো আর্গুমেন্ট আলাদা করতে সেমিকোলন ব্যবহার করে।
অবজেক্ট নিয়ে একটি সংক্ষিপ্ত পরিচিতি।
অনেক প্রোগ্রামিং ভাষার মতোই, অবজেক্ট-এর প্রপার্টি অ্যাক্সেস করতে ডট ব্যবহার করুন
$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"
বন্ধনী অবজেক্ট-এর জন্যও ব্যবহার করা যায়, তবে তখন স্ট্রিং লিটারালের জন্য কোটেশন লাগবে। স্পেস থাকা কী (key) নিয়ে কাজ করার এটি একটি উপায়।
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
{} আর key: value জোড়া দিয়ে আপনি একটি অবজেক্ট তৈরি করতে পারেন।
কী (key) যদি "সহজ" স্ট্রিং হয়, তবে তার চারপাশে কোটেশন লাগে না।
jq -n '{question: (6 * 9), answer: 42}'
এর আউটপুট
{
"question": 54,
"answer": 42
}
কী (key)-কে একটি এক্সপ্রেশন হিসেবে ধরতে হলে, এটিকে প্রথম বন্ধনী দিয়ে মুড়তে হবে (নিচেরটিও উপরের মতোই আউটপুট দেয়)।
echo '[{"key":"question", "value":54}, {"key":"answer", "value":42}]' \
| jq '{(.[0].key): .[0].value, (.[1].key): .[1].value}'
যেমন, file.json-এ আছে
{
"key1": "value1",
"key2": [5, 15, 25]
}
চলুন key2 অ্যারে-র দৈর্ঘ্য বের করি:
$ jq '.key2 | length' file.json
3
আমরা .key2 এক্সপ্রেশনের আউটপুটকে length-এর ইনপুট হিসেবে পাইপ করছি, আর এটি অ্যারে-র এলিমেন্টের সংখ্যা আউটপুট দেয়, তাতে অবাক হওয়ার কিছু নেই।
এই উদাহরণে ইনপুট JSON ডেটা উপেক্ষিত হয় এবং আউটপুটে কোনো প্রভাব ফেলে না:
$ echo '{"answer": 42}' | jq '6 * 9'
54
একটি ফিল্টার একাধিক মান আউটপুট দিতে পারে।
যেমন, .[] ফিল্টার একটি অ্যারে-র প্রতিটি এলিমেন্টকে আলাদা মান হিসেবে আউটপুট দেয়:
$ jq -n -c '[1, 2, 3]'
[1,2,3]
$ jq -n -c '[1, 2, 3] | .[]'
1
2
3
এমন একটি _ফিল্টার_কে অন্যটিতে পাইপ করলে দ্বিতীয় ফিল্টার-টি প্রতিটি মানের জন্য চালিত হবে:
$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6
এটি অনেকটা অন্তর্নিহিত ইটারেশনের মতো।
এই কৌশলটি একবার বুঝে গেলে দেখবেন, খুব শক্তিশালী jq _ফিল্টার_ও বেশ সংক্ষিপ্ত হতে পারে।
প্রথম বন্ধনী দিয়ে সাব-এক্সপ্রেশনগুলো একসাথে দলবদ্ধ করা হয়, যাতে অপারেশনের ক্রম নিশ্চিত হয়, ঠিক অন্য ভাষাগুলোর মতোই।
jq-তে এগুলোর প্রয়োজনীয়তা কিছুটা অপ্রত্যাশিত লাগতে পারে।
যেমন, ধরুন আমরা ২টি এলিমেন্টের একটি অ্যারে বানাতে চাই: ৯-এর বর্গমূল; আর ১ ঘাতে উন্নীত e।
দুটি আলাদা এক্সপ্রেশন হলো 9 | sqrt আর 1 | exp।
আমরা আশা করব আউটপুট হবে অ্যারে [3, 2.7...]
$ jq -n '[ 9|sqrt, 1|exp ]'
[
20.085536923187668,
2.718281828459045
]
আমরা যা আশা করেছিলাম তা পাইনি কেন?
jq এটাকে এভাবে ব্যাখ্যা করে:
[ ((9|sqrt), 1) | exp ]
jq দুটি এলিমেন্টের (3 আর 1) একটি স্ট্রিম তৈরি করে, যেগুলোর প্রত্যেকটিকে exp-কে দেওয়া হয়।
আমাদের নিশ্চিত করতে হবে যে exp ইনপুট হিসেবে কেবল একটি সংখ্যা নেয়।
অর্থাৎ, কমার আগে পাইপটি মূল্যায়িত হয় তা নিশ্চিত করতে হবে।
$ jq -n '[ 9|sqrt, (1|exp) ]'
[
3,
2.718281828459045
]
বেশ গভীরে না গিয়েই (ফাংশন নিয়ে বিস্তারিত আলোচনা আরেকটি অনুশীলনীতে হবে), এখানে কয়েকটি কাজের বিল্ট-ইন ফাংশন দেওয়া হলো:
length
ইনপুট হিসেবে একটি অ্যারে পেলে, অ্যারেটিতে থাকা এলিমেন্টের সংখ্যা আউটপুট দেয়।
$ jq -n '[10, 20, 30, 40] | length'
4
+
এই অপারেটরটি তার অপারেন্ডের টাইপ অনুযায়ী ভিন্ন ভিন্ন কাজ করে: সংখ্যা যোগ করে, স্ট্রিং জোড়া লাগায়, অ্যারে যুক্ত করে, অবজেক্ট একত্র করে।
$ jq -c -n '
3 + 4,
"foo" + "bar",
["a", "b"] + ["c"],
{"m": 10} + {"n": 20}
'
7
"foobar"
["a","b","c"]
{"m":10,"n":20}
add একটি ফাংশন, যা একটি অ্যারে নিয়ে +-এর নিয়মে সব এলিমেন্ট একসাথে যোগ করে একটি মান রিটার্ন করে।
[1, 2, 3] | add আউটপুট দেয় 6.
map
ইনপুট হিসেবে একটি অ্যারে আর আর্গুমেন্ট হিসেবে একটি ফিল্টার পেলে, এমন একটি অ্যারে আউটপুট দেয় যেখানে প্রতিটি এলিমেন্টে ফিল্টারটি প্রয়োগ করা হয়েছে
$ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
[2,4,6,8]
select
কিছু ইনপুট আর আর্গুমেন্ট হিসেবে একটি ফিল্টার পেলে:
null মানটি নয়, সত্যিই কোনো আউটপুট নয়)যেমন, কিছু সংখ্যা দেওয়া থাকলে, ৩ দিয়ে বিভাজ্য সংখ্যাগুলো বেছে নিন
$ jq -n 'range(10) | select(. % 3 == 0)'
0
3
6
9
মনে রাখবেন, range সংখ্যার একটি স্ট্রিম আউটপুট দেয়।
প্রতিটি সংখ্যার জন্য select একবার করে কল করা হবে।
কেবল যে সংখ্যাগুলো এক্সপ্রেশনটি "পাস" করে সেগুলোই আউটপুটে আসে।
আপনাকে প্রায়ই একটি অ্যারের এলিমেন্ট বেছে নিতে হয়। এটি করার কয়েকটি উপায় আছে।
["Anne", "Bob", "Cathy", "Dave"] ইনপুট দিলে, যে নামগুলোর দৈর্ঘ্য ৪ সেগুলো বেছে নিন।
map আর select একসাথে ব্যবহার করুন
map(select(length == 4))
অ্যারেটিকে ভেঙে এলিমেন্টে পরিণত করুন, ওই স্ট্রিমে select করুন, তারপর ফলাফলগুলো একত্র করুন
[ .[] | select(length == 4) ]
মন্তব্য # অক্ষর দিয়ে শুরু হয় আর লাইনের শেষ পর্যন্ত চলতে থাকে।
হঠাৎ আপনার প্যানকেক খেতে খুব ইচ্ছে করছে। আধুনিক ও প্রযুক্তি-মনস্ক একজন মানুষ হিসেবে আপনি প্যানকেকের উপকরণের একটি কেনাকাটার তালিকা JSON ফরম্যাটে লেখেন। তালিকার গঠনটি হলো:
{
"name": "name of shopping list",
"ingredients": [ ...list of ingredients... ],
"optional ingredients": [ ...list of ingredients... ]
}
একটি উপকরণকে JSON অবজেক্ট দিয়ে এভাবে প্রকাশ করা হয়:
{
"item": "flour",
"amount": {
"quantity": 2,
"unit": "cup"
}
}
একটি উপকরণে "substitute" নামের একটি ঐচ্ছিক প্রপার্টি থাকতে পারে, যেখানে থাকে এমন আরেকটি জিনিসের স্ট্রিং যা এর বদলে ব্যবহার করা যায়।
চলুন দেখি jq দিয়ে কেনাকাটার তালিকাটি কীভাবে খতিয়ে দেখা যায়
এমন একটি এক্সপ্রেশন লিখুন যা কেনাকাটার তালিকার "name" এলিমেন্টটি আউটপুট দেয়।
শুধু "আবশ্যক" উপকরণগুলোই গুনুন। ঐচ্ছিক উপকরণগুলো ধরবেন না।
এমন একটি এক্সপ্রেশন লিখুন যা চিনির পরিমাণ আউটপুট দেয়। শুধু সংখ্যার অংশটুকুই দরকার।
কিছু উপকরণ বদলে ব্যবহার করা যায় (যদি আপনার কাছে X উপকরণ না থাকে, তাহলে Y ব্যবহার করতে পারেন)। প্রস্তাবিত উপকরণকে তার বিকল্পের সঙ্গে ম্যাপ করা একটি JSON অবজেক্ট আউটপুট দিন। ম্যাপিংয়ের মধ্যে ঐচ্ছিক উপকরণগুলোও রাখুন।
Exercism-এ সাইন আপ করুন, jq ট্র্যাকের 12টি কনসেপ্ট75টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।