فرض میکنیم که با نحوهی نگارش و انواع دادهی 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"
از کروشهها میتوان برای شیءها هم استفاده کرد، اما در آن صورت برای رشتههای ثابت به گیومه نیاز دارید. این یکی از روشهای کار با کلیدهایی است که فاصله دارند.
$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"
میتوانید یک شیء را با {} و جفتهای key: value بسازید.
دور کلیدهایی که رشتههای «ساده» هستند نیازی به گیومه نیست.
jq -n '{question: (6 * 9), answer: 42}'
خروجی میدهد
{
"question": 54,
"answer": 42
}
برای اینکه با کلید مانند یک عبارت رفتار شود، باید آن را داخل پرانتز بگذارید (مثال زیر هم همان خروجی بالا را میدهد).
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" فهرست خرید را خروجی بدهد.
فقط مواد اولیهی "required" را بشمارید. مواد اولیهی اختیاری را حساب نکنید.
عبارتی بنویسید که مقدار شکر را خروجی بدهد. فقط بخش عددی مورد نظر است.
برخی از مواد اولیه را میتوان جایگزین کرد (اگر مادهی اولیهی X را ندارید، میتوانید از Y استفاده کنید). یک شیء JSON خروجی بدهید که مادهی اولیهی پیشنهادی را به جایگزینش نگاشت میکند. مواد اولیهی اختیاری را هم در این نگاشت بگنجانید.