| JQ(1) | JQ(1) |
نام (NAME)
jq - پردازشگر خط فرمان JSON
خلاصه دستور (SYNOPSIS)
jq [گزینهها...] فیلتر [فایلها...]
دستور jq میتواند دادههای JSON را به روشهای گوناگون با انتخاب کردن، پیمایش، تجمیع و تغییر شکل اسناد JSON دگرگون سازد. برای نمونه، اجرای دستور jq ´map(.price) | add´ یک آرایه از اشیاء JSON را به عنوان ورودی دریافت کرده و مجموع فیلدهای "price" آنها را برمیگرداند.
دستور jq میتواند ورودی متنی را نیز بپذیرد، اما به صورت پیشفرض، جریانی از موجودیتهای JSON (شامل اعداد و سایر مقادیر لغوی) را از stdin میخواند. فاصلههای خالی تنها برای جداسازی موجودیتهایی مانند 1 و 2، یا true و false لازم است. میتوان یک یا چند فایل را مشخص کرد، که در این صورت jq ورودی را از آنها خواهد خواند.
گزینهها (options) در بخش [INVOKING JQ] شرح داده شدهاند؛ آنها عمدتاً به قالببندی ورودی و خروجی مربوط میشوند. فیلتر (filter) به زبان jq نوشته میشود و نحوه دگرگونسازی فایل یا سند ورودی را مشخص میکند.
فیلترها (FILTERS)
یک برنامه jq در واقع یک «فیلتر» است: ورودی را دریافت کرده و خروجی تولید میکند. فیلترهای توکار متعددی برای استخراج یک فیلد خاص از شیء، تبدیل عدد به رشته، یا وظایف استاندارد دیگر وجود دارد.
فیلترها را میتوان به روشهای گوناگونی با هم ترکیب کرد - میتوانید خروجی یک فیلتر را به فیلتر دیگری پایپ کنید، یا خروجی یک فیلتر را درون یک آرایه جمعآوری نمایید.
برخی فیلترها چندین نتیجه تولید میکنند، برای نمونه فیلتری وجود دارد که تمام عناصر آرایه ورودی خود را تولید میکند. پایپ کردن آن فیلتر به فیلتر دوم، فیلتر دوم را به ازای هر عنصر از آرایه اجرا میکند. به طور کلی، کارهایی که در زبانهای دیگر با حلقهها و پیمایش انجام میشوند، در jq با متصل کردن فیلترها به یکدیگر صورت میگیرند.
به یاد داشتن این نکته مهم است که هر فیلتر یک ورودی و یک خروجی دارد. حتی مقادیر لغوی مانند "hello" یا 42 نیز فیلتر هستند - آنها ورودی میگیرند اما همیشه همان مقدار لغوی را در خروجی تولید میکنند. عملیاتی که دو فیلتر را ترکیب میکنند، مانند جمع، معمولاً همان ورودی یکسان را به هر دو فیلتر میدهند و نتایج را با یکدیگر ترکیب میکنند. بنابراین، میتوانید یک فیلتر میانگینگیری را به صورت add / length پیادهسازی کنید - که آرایه ورودی را هم به فیلتر add و هم به فیلتر length میدهد و سپس عمل تقسیم را انجام میدهد.
اما هنوز برای این کار زود است. :) بیایید با موضوعی سادهتر آغاز کنیم:
فراخوانی JQ (INVOKING JQ)
فیلترهای jq روی جریانی از دادههای JSON اجرا میشوند. ورودی jq به صورت توالیای از مقادیر JSON جداشده با فاصله خالی پردازش میشود که یکییکی از فیلتر ارائهشده عبور داده میشوند. خروجی(های) فیلتر در خروجی استاندارد، به صورت توالیای از دادههای JSON جداشده با خط جدید نوشته میشوند.
سادهترین و رایجترین فیلتر (یا برنامه jq)، عملگر همانی . است که ورودیهای پردازشگر jq را عینا در جریان خروجی کپی میکند. از آنجا که رفتار پیشفرض پردازشگر jq خواندن متنهای JSON از جریان ورودی و زیباسازی چاپ (pretty-print) خروجیها است، کاربرد اصلی برنامه . اعتبارسنجی و چاپ زیبای ورودیها میباشد. زبان برنامهنویسی jq بسیار غنی است و امکاناتی بسیار فراتر از صرفاً اعتبارسنجی و زیباسازی چاپ فراهم میکند.
نکته: توجه به قواعد نقلقول در شل بسیار مهم است. به عنوان یک قاعده کلی، همیشه بهتر است برنامه jq را درون کوتیشن قرار دهید (با کاراکترهای تککوتیشن در شلهای یونیکس)، چرا که کاراکترهای زیادی که در jq معنای خاصی دارند، متاکاراکترهای شل نیز محسوب میشوند. برای نمونه، دستور jq "foo" در بیشتر شلهای یونیکس شکست خواهد خورد چون مشابه jq foo عمل میکند، که عموماً به دلیل foo is not defined با شکست مواجه میشود. هنگام استفاده از خط فرمان ویندوز (cmd.exe) بهتر است برنامه jq را در صورت ارسال مستقیم در خط فرمان (به جای گزینه -f program-file) درون دابلکوتیشن قرار دهید، اما در این حالت دابلکوتیشنهای درون برنامه jq نیازمند اسکیپ با بکاسلش هستند. هنگام استفاده از پاورشل (powershell.exe) یا پاورشل کور (pwsh/pwsh.exe)، از کاراکترهای تککوتیشن در اطراف برنامه jq و دابلکوتیشنهای اسکیپشده با بکاسلش (\") درون برنامه jq استفاده کنید.
- شلهای یونیکس: jq ´.["foo"]´
- پاورشل: jq ´.[\"foo\"]´
- خط فرمان ویندوز: jq ".[\"foo\"]"
نکته: دستور jq از توابع تعریفشده توسط کاربر پشتیبانی میکند، اما هر برنامه jq باید یک عبارت سطح بالا (top-level) داشته باشد.
میتوانید با استفاده از برخی گزینههای خط فرمان، نحوه خواندن و نوشتن ورودی و خروجی در jq را تغییر دهید:
- هیچ ورودی خوانده نمیشود. در عوض، فیلتر یکبار با استفاده از null به عنوان ورودی اجرا میشود. این گزینه هنگام استفاده از jq به عنوان یک ماشینحساب ساده یا ساخت دادههای JSON از ابتدا کاربرد دارد.
- ورودی به عنوان JSON تجزیه نمیشود. در عوض، هر خط از متن به عنوان یک رشته به فیلتر ارسال میگردد. در صورت ترکیب با گزینه --slurp، تمام ورودی به عنوان یک رشته طولانی منفرد به فیلتر پاس داده میشود.
- به جای اجرای فیلتر برای هر شیء JSON در ورودی، کل جریان ورودی را درون یک آرایه بزرگ میخواند و فیلتر را تنها یک بار اجرا میکند.
- به صورت پیشفرض، jq خروجی JSON را با فرمت زیبا چاپ میکند. استفاده از این گزینه موجب خروجی فشردهتر با قرار دادن هر شیء JSON در یک خط مجزا میشود.
- با این گزینه، اگر نتیجه فیلتر یک رشته باشد، به جای قالببندی شدن به عنوان رشته JSON همراه با کوتیشن، مستقیماً در خروجی استاندارد نوشته میشود. این کار برای برقراری ارتباط فیلترهای jq با سیستمهای غیر مبتنی بر JSON کاربردی است.
- مشابه -r اما jq پس از هر خروجی به جای خط جدید، نویسه NUL چاپ میکند. این ویژگی زمانی مفید است که مقادیر خروجی حاوی خط جدید باشند. اگر مقدار خروجی حاوی NUL باشد، jq با کد غیر صفر خارج میشود.
- مشابه -r اما jq پس از هر خروجی یک خط جدید چاپ نمیکند.
- معمولاً jq کدهای یونیکد غیر ASCII را به صورت UTF-8 چاپ میکند، حتی اگر ورودی آنها را به عنوان توالیهای اسکیپ (مانند "\u03bc") مشخص کرده باشد. با استفاده از این گزینه، میتوانید jq را وادار کنید خروجی کاملاً ASCII تولید کند که در آن هر نویسه غیر ASCII با توالی اسکیپ معادل جایگزین شده است.
- فیلدهای هر شیء را همراه با کلیدهای مرتبشده در خروجی چاپ میکند.
- به صورت پیشفرض، jq اگر در ترمینال بنویسد JSON را رنگی چاپ میکند. میتوانید با استفاده از -C آن را مجبور به تولید خروجی رنگی حتی هنگام نوشتن در پایپ یا فایل کنید، و با -M رنگ را غیرفعال سازید. هنگامی که متغیر محیطی NO_COLOR خالی نباشد، jq به صورت پیشفرض خروجی رنگی را غیرفعال میکند، اما میتوانید با -C آن را فعال نمایید.
- رنگها را میتوان با متغیر محیطی JQ_COLORS پیکربندی کرد (به زیر مراجعه کنید).
- برای هر سطح تورفتگی به جای دو فاصله از تب استفاده میکند.
- از تعداد فاصلههای مشخصشده (حداکثر 7) برای تورفتگی استفاده میکند.
- خروجی را پس از چاپ هر شیء JSON بلافاصله تخلیه (flush) میکند (هنگامی که منبع دادهای کند را به jq پایپ کرده و خروجی jq را به جای دیگری میفرستید بسیار مفید است).
- ورودی را به شیوه جریانی تجزیه کرده و آرایههایی از مسیر و مقادیر برگ (اسکالرها و آرایهها یا اشیاء خالی) را خروجی میدهد. برای نمونه، "a" تبدیل به [[],"a"] میشود و [[],"a",["b"]] تبدیل به [[0],[]]، [[1],"a"] و [[2,0],"b"] میگردد.
- این گزینه برای پردازش ورودیهای بسیار بزرگ سودمند است. از این گزینه همراه با فیلترسازی و ساختار دستور reduce و foreach برای تجمیع تدریجی ورودیهای بزرگ استفاده کنید.
- مشابه --stream، اما ورودیهای نامعتبر JSON مقادیر آرایهای تولید میکنند که عنصر نخست خطا و عنصر دوم مسیر است. برای نمونه، ["a",n] مقدار ["Invalid literal at line 1, column 7",[1]] را تولید میکند.
- به همراه خود --stream را فعال میکند. ورودیهای نامعتبر JSON در حالت --stream بدون --stream-errors هیچ مقدار خطایی تولید نمیکنند.
- از طرح نوع رسانهای application/json-seq برای جداسازی متنهای JSON در ورودی و خروجی jq استفاده میکند. این به معنای آن است که یک نویسه جداکننده رکورد اسکی (RS) پیش از هر مقدار در خروجی چاپ میشود و یک خط جدید اسکی (LF) پس از هر خروجی چاپ میگردد. متنهای ورودی JSON که در تجزیه با شکست مواجه میشوند نادیده گرفته شده (اما درباره آنها هشدار داده میشود)، و تمام ورودیهای بعدی تا RS بعدی دور ریخته میشوند. این حالت همچنین خروجی jq بدون گزینه --seq را تجزیه میکند.
- فیلتر را به جای خط فرمان، مانند گزینه -f در دستور awk، از یک فایل میخواند. این امر باعث میشود آرگومان فیلتر به عنوان نام یک فایل تفسیر شود، نه به عنوان سورس برنامه.
- پوشه directory را به ابتدای فهرست جستجوی ماژولها اضافه میکند. در صورت استفاده از این گزینه، هیچ فهرست جستجوی توکاری استفاده نخواهد شد. به بخش ماژولها در ادامه مراجعه کنید.
- این گزینه مقداری را به عنوان یک متغیر از پیش تعریفشده به برنامه jq پاس میدهد. اگر jq را با --arg foo bar اجرا کنید، $foo در برنامه در دسترس خواهد بود و مقدار "bar" را خواهد داشت. توجه داشته باشید که با value به عنوان رشته رفتار میشود، بنابراین --arg foo 123 مقدار $foo را به "123" مقید میکند.
- آرگومانهای نامدار همچنین به عنوان $ARGS.named برای برنامه jq در دسترس هستند. هنگامی که نام یک شناسه معتبر نباشد، این تنها راه دسترسی به آن است.
- این گزینه یک مقدار کدگذاریشده به صورت JSON را به عنوان یک متغیر از پیش تعریفشده به برنامه jq پاس میدهد. اگر jq را با --argjson foo 123 اجرا کنید، $foo در برنامه در دسترس بوده و مقدار 123 را خواهد داشت.
- این گزینه تمام متنهای JSON درون فایل نامبرده را خوانده و آرایهای از مقادیر تجزیهشده JSON را به متغیر سراسری ارائهشده مقید میکند. اگر jq را با --slurpfile foo bar اجرا کنید، $foo در برنامه در دسترس خواهد بود و دارای آرایهای است که عناصر آن متناظر با متنهای موجود در فایل bar هستند.
- این گزینه فایل نامبرده را میخواند و محتوای آن را به متغیر سراسری دادهشده مقید میسازد. اگر jq را با --rawfile foo bar اجرا کنید، $foo در برنامه در دسترس بوده و دارای رشتهای است که محتوای آن برابر با متن موجود در فایل bar تنظیم شده است.
- آرگومانهای باقیمانده، آرگومانهای رشتهای موضعی هستند. این آرگومانها به صورت $ARGS.positional[] برای برنامه jq در دسترس خواهند بود.
- آرگومانهای باقیمانده، آرگومانهای متنی موضعی از نوع JSON هستند. این آرگومانها به صورت $ARGS.positional[] برای برنامه jq در دسترس خواهند بود.
- وضعیت خروج jq را روی 0 تنظیم میکند اگر آخرین مقدار خروجی نه false باشد و نه null، روی 1 اگر آخرین مقدار خروجی false یا null باشد، یا روی 4 اگر هیچ نتیجه معتبری هرگز تولید نشده باشد. در حالت عادی jq در صورت بروز هرگونه خطای استفاده یا خطای سیستمی با 2، در صورت خطای کامپایل برنامه jq با 3، یا در صورت اجرای موفق برنامه jq با 0 خارج میشود.
- روش دیگر برای تنظیم وضعیت خروج، استفاده از تابع توکار halt_error است.
- کاربران ویندوز که از WSL، MSYS2 یا Cygwin استفاده میکنند، در صورت استفاده از نسخه بومی jq.exe باید از این گزینه استفاده کنند، در غیر این صورت jq خطوط جدید (LF) را به بازگشت مکاننما و خط جدید (CRLF) تبدیل میکند.
- نسخه jq را در خروجی چاپ کرده و با وضعیت صفر خارج میشود.
- پیکربندی ساخت jq را در خروجی چاپ کرده و با وضعیت صفر خارج میشود. این خروجی هیچ فرمت یا ساختار پشتیبانیشدهای ندارد و ممکن است در نسخههای بعدی بدون اطلاع قبلی تغییر کند.
- راهنمای jq را در خروجی چاپ کرده و با وضعیت صفر خارج میشود.
- --:
- پردازش آرگومانها را خاتمه میدهد. آرگومانهای باقیمانده به عنوان گزینه تفسیر نمیشوند.
- آزمونهای موجود در فایل دادهشده یا ورودی استاندارد را اجرا میکند. این گزینه باید آخرین گزینه ارائهشده باشد و از تمام گزینههای پیشین تبعیت نمیکند. ورودی شامل خطوط توضیح، خطوط خالی، و خطوط برنامه است که پس از آنها یک خط ورودی، به تعداد خطوط خروجی مورد انتظار (یک خط به ازای هر خروجی)، و یک خط خالی پایانبخش قرار دارد. آزمونهای شکست کامپایل با خطی شامل صرفاً %%FAIL آغاز میشوند، سپس خطی شامل برنامه برای کامپایل، و پس از آن خطی شامل پیام خطا برای مقایسه با مقدار واقعی قرار میگیرد.
- توجه داشته باشید که این گزینه ممکن است به گونهای تغییر کند که سازگاری با نسخههای پیشین حفظ نشود.
فیلترهای پایه (BASIC FILTERS)
همانی: .
سادهترین فیلتر مطلق، . است. این فیلتر ورودی خود را میگیرد و همان مقدار را به عنوان خروجی تولید میکند؛ یعنی همان عملگر همانی.
از آنجا که jq به صورت پیشفرض تمام خروجیها را به صورت زیبا چاپ میکند، یک برنامه ساده شامل صرفاً . میتواند برای قالببندی خروجی JSON ناشی از، مثلاً، curl استفاده شود.
اگرچه فیلتر همانی هرگز مقدار ورودی خود را تغییر نمیدهد، اما پردازش jq گاهی میتواند طوری به نظر برسد که گویی چنین تغییری رخ داده است. برای نمونه، با استفاده از پیادهسازی فعلی jq، مشاهده میکنیم که عبارت:
-
1E1234567890 | .
در حداقل یک پلتفرم مقدار 1.7976931348623157e+308 را تولید میکند. دلیل این امر آن است که این نسخه خاص از jq در فرآیند تجزیه عدد، آن را به نمایش ممیز شناور با دقت مضاعف IEEE754 تبدیل کرده و دچار افت دقت شده است.
روشی که jq اعداد را پردازش میکند در طول زمان تغییر یافته و احتمال تغییرات بیشتر در چارچوب استانداردهای مرتبط با JSON وجود دارد. علاوه بر این، گزینههای پیکربندی ساخت میتوانند نحوه پردازش اعداد توسط jq را تغییر دهند.
بنابراین توضیحات زیر با این درک ارائه میشوند که صرفاً توصیفکننده نسخه فعلی jq هستند و نباید به عنوان یک استاندارد قطعی تلقی شوند:
(1) هرگونه عملیات حسابی روی عددی که پیشتر به نمایش با دقت مضاعف IEEE754 تبدیل نشده باشد، تبدیل به نمایش IEEE754 را رقم خواهد زد.
(2) دستور jq تلاش میکند دقت دهدهی اصلی اعداد لغوی را حفظ کند (اگر گزینه پیکربندی ساخت --disable-decnum استفاده نشده باشد)، اما در عباراتی نظیر 1E1234567890، چنانچه نما بیش از حد بزرگ باشد، دقت از بین خواهد رفت.
(3) مقایسهها در صورت در دسترس بودن، با استفاده از نمایش اعشاری بزرگ و کوتاهنشده اعداد انجام میشوند، همانطور که در یکی از مثالهای زیر نشان داده شده است.
مثالهای زیر از تابع توکار have_decnum به منظور نشان دادن اثرات مورد انتظار استفاده یا عدم استفاده از گزینه پیکربندی ساخت --disable-decnum استفاده میکنند، و همچنین برای اینکه آزمونهای خودکار مشتقشده از این مثالها بدون توجه به استفاده یا عدم استفاده از آن گزینه با موفقیت پاس شوند.
-
jq ´.´ "Hello, world!" => "Hello, world!" jq ´.´ 0.12345678901234567890123456789 => 0.12345678901234567890123456789 jq ´[., tojson] == if have_decnum then [12345678909876543212345,"12345678909876543212345"] else [12345678909876543000000,"12345678909876543000000"] end´ 12345678909876543212345 => true jq ´[1234567890987654321,-1234567890987654321 | tojson] == if have_decnum then ["1234567890987654321","-1234567890987654321"] else ["1234567890987654400","-1234567890987654400"] end´ null => true jq ´. < 0.12345678901234567890123456788´ 0.12345678901234567890123456789 => false jq ´map([., . == 1]) | tojson == if have_decnum then "[[1,true],[1.000,true],[1.0,true],[1.00,true]]" else "[[1,true],[1,true],[1,true],[1,true]]" end´ [1, 1.000, 1.0, 100e-2] => true jq ´. as $big | [$big, $big + 1] | map(. > 10000000000000000000000000000000) | . == if have_decnum then [true, false] else [false, false] end´ 10000000000000000000000000000001 => true
شناسه-نمایه شیء: .foo, .foo.bar
سادهترین فیلتر کاربردی به شکل .foo است. هنگامی که یک شیء JSON (معروف به دیکشنری یا جدول هش) به عنوان ورودی داده شود، .foo مقدار موجود در کلید "foo" را در صورت وجود کلید تولید میکند، و در غیر این صورت null برمیگرداند.
فیلتری به شکل .foo.bar معادل .foo | .bar است.
ساختار .foo تنها برای کلیدهای ساده و شبهشناسه کار میکند، یعنی کلیدهایی که تماماً از نویسههای حرفی-عددی و خط زیرین تشکیل شدهاند و با رقم شروع نمیشوند.
اگر کلید شامل نویسههای خاص باشد یا با یک رقم آغاز شود، باید آن را مانند ."foo$" یا .["foo$"] درون نقلقول دوتایی قرار دهید.
برای نمونه، .["foo::bar"] و .["foo.bar"] کار میکنند در حالی که .foo::bar کار نمیکند.
-
jq ´.foo´ {"foo": 42, "bar": "less interesting data"} => 42 jq ´.foo´ {"notfoo": true, "alsonotfoo": false} => null jq ´.["foo"]´ {"foo": 42} => 42
شناسه/نمایه اختیاری شیء: .foo?
دقیقاً مانند .foo، اما زمانی که . یک شیء نباشد خطایی در خروجی صادر نمیکند.
-
jq ´.foo?´ {"foo": 42, "bar": "less interesting data"} => 42 jq ´.foo?´ {"notfoo": true, "alsonotfoo": false} => null jq ´.["foo"]?´ {"foo": 42} => 42 jq ´[.foo?]´ [1,2] => []
نمایه شیء: .[<string>]
همچنین میتوانید با استفاده از ساختاری مانند .["foo"] فیلدهای یک شیء را بررسی کنید (.foo در بالا شکل خلاصهشده این حالت است، اما تنها برای رشتههای شبهشناسه کاربرد دارد).
نمایه آرایه: .[<number>]
هنگامی که مقدار نمایه یک عدد صحیح باشد، .[<number>] میتواند آرایهها را نمایه کند. آرایهها مبتنی بر صفر هستند، بنابراین .[2] سومین عنصر را برمیگرداند.
نمایههای منفی نیز مجاز هستند، بهطوریکه -1 به آخرین عنصر، -2 به یکی مانده به آخرین عنصر و الی آخر اشاره دارد.
-
jq ´.[0]´ [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => {"name":"JSON", "good":true} jq ´.[2]´ [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => null jq ´.[-2]´ [1,2,3] => 2
برش آرایه/رشته: .[<number>:<number>]
ساختار .[<number>:<number>] میتواند برای بازگرداندن یک زیرآرایه از یک آرایه یا یک زیررشته از یک رشته استفاده شود. آرایه بازگرداندهشده توسط .[10:15] دارای طول ۵ خواهد بود که شامل عناصر از نمایه ۱۰ (شامل خود آن) تا نمایه ۱۵ (بدون خود آن) است. هر یک از نمایهها میتوانند منفی باشند (که در این صورت از انتهای آرایه به عقب شمارش میشود) یا حذف شوند (که در این صورت به ابتدا یا انتهای آرایه اشاره دارد). نمایهها مبتنی بر صفر هستند.
-
jq ´.[2:4]´ ["a","b","c","d","e"] => ["c", "d"] jq ´.[2:4]´ "abcdefghi" => "cd" jq ´.[:3]´ ["a","b","c","d","e"] => ["a", "b", "c"] jq ´.[-2:]´ ["a","b","c","d","e"] => ["d", "e"]
تکرارکننده مقادیر آرایه/شیء: .[]
اگر از ساختار .[index] استفاده کنید، اما نمایه را کاملاً حذف نمایید، تمام عناصر یک آرایه بازگردانده میشود. اجرای .[] با ورودی [1,2,3] اعداد را به عنوان سه نتیجه جداگانه تولید میکند، نه به عنوان یک آرایه منفرد. فیلتری به شکل .foo[] معادل .foo | .[] است.
همچنین میتوانید از این ساختار روی یک شیء استفاده کنید، و تمام مقادیر آن شیء را بازمیگرداند.
توجه داشته باشید که عملگر تکرارکننده یک تولیدکننده مقادیر است.
-
jq ´.[]´ [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => {"name":"JSON", "good":true}, {"name":"XML", "good":false} jq ´.[]´ [] => jq ´.foo[]´ {"foo":[1,2,3]} => 1, 2, 3 jq ´.[]´ {"a": 1, "b": 1} => 1, 1
.[]?
مانند .[]، اما اگر . یک آرایه یا شیء نباشد هیچ خطایی در خروجی صادر نخواهد شد. فیلتری به شکل .foo[]? معادل .foo | .[]? است.
کاما: ,
اگر دو فیلتر با کاما از هم جدا شوند، ورودی یکسانی به هر دو داده میشود و جریان مقادیر خروجی دو فیلتر به ترتیب الحاق میشوند: ابتدا تمام خروجیهای حاصل از عبارت سمت چپ، و سپس تمام خروجیهای حاصل از سمت راست. برای نمونه، فیلتر .foo, .bar هر دو فیلد "foo" و "bar" را به عنوان خروجیهای جداگانه تولید میکند.
عملگر , یکی از راههای ساخت تولیدکنندهها است.
-
jq ´.foo, .bar´ {"foo": 42, "bar": "something else", "baz": true} => 42, "something else" jq ´.user, .projects[]´ {"user":"stedolan", "projects": ["jq", "wikiflow"]} => "stedolan", "jq", "wikiflow" jq ´.[4,2]´ ["a","b","c","d","e"] => "e", "c"
پایپ: |
عملگر | با هدایت خروجی(های) عبارت سمت چپ به ورودی عبارت سمت راست، دو فیلتر را ترکیب میکند. اگر با لوله (Pipe) در پوسته یونیکس آشنایی داشته باشید، عملکردی مشابه آن دارد.
اگر عبارت سمت چپ چندین نتیجه تولید کند، عبارت سمت راست برای تکتک آن نتایج اجرا خواهد شد. بنابراین عبارت .[] | .foo فیلد "foo" از هر عنصر آرایه ورودی را بازیابی میکند. این یک حاصلضرب دکارتی است که میتواند شگفتانگیز باشد.
توجه داشته باشید که .a.b.c همانند .a | .b | .c است.
همچنین توجه داشته باشید که . مقدار ورودی در آن مرحله خاص از یک «خط لوله» است، مشخصاً: جایی که عبارت . ظاهر میشود. بنابراین .a | . | .b مانند .a.b است، زیرا . میانی به هر مقداری که توسط .a تولید شده ارجاع دارد.
-
jq ´.[] | .name´ [{"name":"JSON", "good":true}, {"name":"XML", "good":false}] => "JSON", "XML"
پرانتزها (Parenthesis)
پرانتزها دقیقاً مانند هر زبان برنامهنویسی معمول دیگر به عنوان عملگر گروهبندی عمل میکنند.
-
jq ´(. + 2) * 5´ 1 => 15
انواع داده و مقادیر (TYPES AND VALUES)
ابزار jq از همان مجموعه انواع داده JSON پشتیبانی میکند - اعداد، رشتهها، مقادیر بولی، آرایهها، اشیاء (که در اصطلاح JSON همان هشهایی با کلیدهای رشتهای هستند)، و "null".
مقادیر بولی، null، رشتهها و اعداد به همان شیوه JSON نوشته میشوند. درست مانند هر چیز دیگری در jq، این مقادیر ساده یک ورودی میگیرند و یک خروجی تولید میکنند - 42 یک عبارت معتبر در jq است که ورودی را میگیرد، آن را نادیده میگیرد و به جای آن 42 را برمیگرداند.
اعداد در jq در سطح داخلی با تقریب دقت مضاعف IEEE754 نمایش داده میشوند. هرگونه عملیات محاسباتی روی اعداد، چه مقادیر صریح باشند چه حاصل فیلترهای قبلی، یک نتیجه ممیز شناور با دقت مضاعف تولید میکند.
با این حال، هنگام تجزیه یک مقدار صریح، jq رشته صریح اصلی را ذخیره میکند. اگر هیچ تغییری روی این مقدار اعمال نشود، حتی اگر تبدیل به دقت مضاعف منجر به کاهش دقت شود، مقدار به شکل اصلی خود به خروجی انتقال مییابد.
ساخت آرایه: []
همانند JSON، از [] برای ساخت آرایهها استفاده میشود، مانند [1,2,3]. عناصر آرایهها میتوانند هر عبارت jq باشند، از جمله یک خط لوله. تمام نتایج تولیدشده توسط تمامی عبارتها در یک آرایه بزرگ جمعآوری میشوند. میتوانید از آن برای ساخت یک آرایه از تعداد مشخصی مقدار (مانند [.foo, .bar, .baz]) یا برای «جمعآوری» تمام نتایج یک فیلتر در یک آرایه (مانند [.items[].name]) استفاده کنید.
به محض درک عملگر ","، میتوانید ساختار آرایه jq را از زاویه دیگری ببینید: عبارت [1,2,3] از نحو توکار برای آرایههای جداشده با کاما استفاده نمیکند، بلکه عملگر [] (جمعآوری نتایج) را روی عبارت 1,2,3 (که سه نتیجه جداگانه تولید میکند) اعمال مینماید.
اگر فیلتری به نام X داشته باشید که چهار نتیجه تولید کند، عبارت [X] یک نتیجه واحد تولید خواهد کرد که آرایهای از چهار عنصر است.
-
jq ´[.user, .projects[]]´ {"user":"stedolan", "projects": ["jq", "wikiflow"]} => ["stedolan", "jq", "wikiflow"] jq ´[ .[] | . * 2]´ [1, 2, 3] => [2, 4, 6]
ساخت شیء: {}
همانند JSON، از {} برای ساخت اشیاء (همان دیکشنریها یا هشها) استفاده میشود، مانند: {"a": 42, "b": 17}.
اگر کلیدها «شبهشناسه» باشند، میتوان گیومهها را حذف کرد، مانند {a:42, b:17}. ارجاع به متغیرها به عنوان عبارتهای کلید از مقدار متغیر به عنوان کلید استفاده میکنند. عبارتهای کلید به جز مقادیر صریح ثابت، شناسهها یا ارجاع به متغیرها، باید درون پرانتز قرار گیرند، برای نمونه: {("a"+"b"):59}.
مقدار میتواند هر عبارتی باشد (هرچند اگر برای مثال شامل دونقطه باشد ممکن است لازم باشد آن را در پرانتز قرار دهید) که بر ورودی عبارت {} اعمال میشود (به یاد داشته باشید، تمام فیلترها دارای ورودی و خروجی هستند).
-
{foo: .bar}
در صورتی که شیء JSON به صورت {"bar":42, "baz":43} به عنوان ورودی داده شود، شیء JSON برابر با {"foo": 42} را تولید خواهد کرد. میتوانید از این ساختار برای انتخاب فیلدهای خاصی از یک شیء استفاده کنید: اگر ورودی شیئی با فیلدهای "user"، "title"، "id" و "content" باشد و شما فقط "user" و "title" را بخواهید، میتوانید بنویسید
-
{user: .user, title: .title}
از آنجا که این کار بسیار متداول است، یک نحو میانبر برای آن وجود دارد: {user, title}.
اگر یکی از عبارتها چندین نتیجه تولید کند، چندین دیکشنری تولید خواهد شد. اگر ورودی
-
{"user":"stedolan","titles":["JQ Primer", "More JQ"]}
باشد، آنگاه عبارت
-
{user, title: .titles[]}
دو خروجی تولید خواهد کرد:
-
{"user":"stedolan", "title": "JQ Primer"} {"user":"stedolan", "title": "More JQ"}
قرار دادن پرانتز دور کلید به این معنی است که به عنوان یک عبارت ارزیابی خواهد شد. با همان ورودی بالا،
-
{(.user): .titles}
تولید میکند
-
{"stedolan": ["JQ Primer", "More JQ"]}
ارجاعات به متغیرها به عنوان کلید از مقدار متغیر به عنوان کلید استفاده میکنند. بدون یک مقدار، نام متغیر تبدیل به کلید و مقدار آن تبدیل به مقدار فیلد میشود،
-
"f o o" as $foo | "b a r" as $bar | {$foo, $bar:$foo}
تولید میکند:
-
{"foo":"f o o","b a r":"f o o"} jq ´{user, title: .titles[]}´ {"user":"stedolan","titles":["JQ Primer", "More JQ"]} => {"user":"stedolan", "title": "JQ Primer"}, {"user":"stedolan", "title": "More JQ"} jq ´{(.user): .titles}´ {"user":"stedolan","titles":["JQ Primer", "More JQ"]} => {"stedolan": ["JQ Primer", "More JQ"]}
پیمایش بازگشتی: ..
به صورت بازگشتی در . پایین میرود و تمام مقادیر را تولید میکند. این رفتار مشابه تابع توکار بدون آرگومان recurse است (به زیر مراجعه کنید). هدف از این عملگر شباهت به عملگر // در XPath است. توجه داشته باشید که ..a کار نمیکند؛ به جای آن از .. | .a استفاده کنید. در مثال زیر ما از .. | .a? برای یافتن تمام مقادیر کلیدهای شیء "a" در هر شیء موجود در "زیر" . استفاده میکنیم.
این ویژگی به ویژه در ترکیب با path(EXP) (همچنین به زیر مراجعه کنید) و عملگر ? بسیار کاربردی است.
-
jq ´.. | .a?´ [[{"a":1}]] => 1
عملگرها و توابع توکار (BUILTIN OPERATORS AND FUNCTIONS)
برخی از عملگرهای jq (برای نمونه، +) بسته به نوع آرگومانهای خود (آرایهها، اعداد و غیره) کارهای متفاوتی انجام میدهند. با این حال، jq هرگز تبدیل نوع ضمنی (implicit type conversion) انجام نمیدهد. اگر سعی کنید یک رشته را با یک شیء جمع کنید، با پیام خطا مواجه خواهید شد و هیچ نتیجهای دریافت نمیکنید.
لطفاً توجه داشته باشید که تمام اعداد به نمایش ممیز شناور با دقت مضاعف IEEE754 تبدیل میشوند. عملگرهای حسابی و منطقی با این مقادیر ممیز شناور تبدیلشده کار میکنند. نتایج تمام این عملیات نیز به دقت مضاعف محدود میشود.
تنها استثنا در این رفتار برای اعداد، نمونهای از مقدار لغوی اصلی عدد است. هنگامی که عددی که در ابتدا به صورت لغوی ارائه شده است تا پایان برنامه هرگز تغییر داده نشود، در خروجی به همان شکل لغوی اصلی خود چاپ میشود. این موضوع همچنین شامل مواردی میشود که در آن مقدار لغوی اصلی در صورت تبدیل به عدد ممیز شناور با دقت مضاعف IEEE754 کوتاه (truncate) میشد.
جمع: +
عملگر + دو فیلتر را دریافت کرده، هر دوی آنها را روی همان ورودی اعمال میکند و نتایج را با یکدیگر جمع میکند. معنای "جمع کردن" به نوعهای درگیر بستگی دارد:
- اعداد با محاسبات حسابی معمولی جمع میشوند.
- آرایهها با پیوستن به یکدیگر در قالب یک آرایه بزرگتر جمع میشوند.
- رشتهها با اتصال به یکدیگر در قالب یک رشته بزرگتر جمع میشوند.
- اشیاء با ادغام شدن جمع میشوند، یعنی با درج تمام جفتهای کلید-مقدار از هر دو شیء در یک شیء ترکیبی واحد. اگر هر دو شیء حاوی مقداری برای یک کلید یکسان باشند، شیء سمت راست + برنده است. (برای ادغام بازگشتی از عملگر * استفاده کنید.)
مقدار null میتواند با هر مقداری جمع شود، و مقدار دیگر را بدون تغییر برمیگرداند.
-
jq ´.a + 1´ {"a": 7} => 8 jq ´.a + .b´ {"a": [1,2], "b": [3,4]} => [1,2,3,4] jq ´.a + null´ {"a": 1} => 1 jq ´.a + 1´ {} => 1 jq ´{a: 1} + {b: 2} + {c: 3} + {a: 42}´ null => {"a": 42, "b": 2, "c": 3}
تفریق: -
علاوه بر تفریق حسابی معمولی روی اعداد، عملگر - میتواند روی آرایهها استفاده شود تا تمام موارد وقوع عناصر آرایه دوم را از آرایه اول حذف کند.
-
jq ´4 - .a´ {"a":3} => 1 jq ´. - ["xml", "yaml"]´ ["xml", "yaml", "json"] => ["json"]
ضرب، تقسیم، باقیمانده: *, /, %
این عملگرهای میانوند (infix) در صورت دریافت دو عدد، رفتار مورد انتظار را دارند. تقسیم بر صفر خطا ایجاد میکند. عبارت x % y باقیمانده تقسیم x بر y را محاسبه میکند.
ضرب یک رشته در یک عدد، تکرار و اتصال آن رشته را به همان تعداد دفعات تولید میکند. عبارت "x" * 0 مقدار "" را تولید میکند.
تقسیم یک رشته بر رشته دیگر، رشته اول را با استفاده از رشته دوم به عنوان جداکننده تکهتکه میکند.
ضرب دو شیء آنها را به صورت بازگشتی ادغام میکند: این عمل مشابه جمع کار میکند اما اگر هر دو شیء حاوی مقداری برای یک کلید یکسان باشند و آن مقادیر شیء باشند، آن دو شیء با همان راهبرد با یکدیگر ادغام میشوند.
-
jq ´10 / . * 3´ 5 => 6 jq ´. / ", "´ "a, b,c,d, e" => ["a","b,c,d","e"] jq ´{"k": {"a": 1, "b": 2}} * {"k": {"a": 0,"c": 3}}´ null => {"k": {"a": 0, "b": 2, "c": 3}} jq ´.[] | (1 / .)?´ [1,0,-1] => 1, -1
abs
تابع توکار abs به صورت ساده اینگونه تعریف میشود: if . < 0 then - . else . end.
برای ورودیهای عددی، این همان مقدار مطلق (قدر مطلق) است. برای پیامدهای این تعریف برای ورودی عددی، بخش مربوط به فیلتر همانی را ببینید.
برای محاسبه مقدار مطلق یک عدد به عنوان یک عدد ممیز شناور، میتوانید از fabs استفاده کنید.
-
jq ´map(abs)´ [-10, -1.1, -1e-1] => [10,1.1,1e-1]
length
تابع توکار length طول انواع مختلف مقادیر را به دست میآورد:
- طول یک رشته برابر با تعداد کدنقاط (codepoints) یونیکد موجود در آن است (که اگر صرفاً ASCII باشد، با طول کدگذاریشده JSON آن به بایت برابر خواهد بود).
- طول یک عدد برابر با مقدار مطلق (قدر مطلق) آن است.
- طول یک آرایه برابر با تعداد عناصر آن است.
- طول یک شیء برابر با تعداد جفتهای کلید-مقدار آن است.
- طول null برابر با صفر است.
- استفاده از length روی یک بولی (boolean) یک خطا است.
-
jq ´.[] | length´ [[1,2], "string", {"a":2}, null, -5] => 2, 6, 1, 0, 5
utf8bytelength
تابع توکار utf8bytelength تعداد بایتهای استفادهشده برای کدگذاری یک رشته به UTF-8 را در خروجی میدهد.
-
jq ´utf8bytelength´ "\u03bc" => 2
keys, keys_unsorted
تابع توکار keys، زمانی که یک شیء به آن داده میشود، کلیدهای آن را درون یک آرایه برمیگرداند.
کلیدها به صورت "الفبایی" و بر اساس ترتیب کدنقاط یونیکد مرتب میشوند. این ترتیبی نیست که در زبان خاصی معنای خاصی داشته باشد، اما میتوانید مطمئن باشید که برای هر دو شیء با مجموعه کلیدهای یکسان، صرفنظر از تنظیمات محلی (locale)، ترتیبی یکسان خواهد بود.
هنگامی که یک آرایه به keys داده میشود، اندیسهای معتبر برای آن آرایه را برمیگرداند: اعداد صحیح از 0 تا length-1.
تابع keys_unsorted درست مانند keys است، اما اگر ورودی یک شیء باشد، کلیدها مرتب نخواهند شد و در عوض کلیدها تقریباً به ترتیب درج خواهند بود.
-
jq ´keys´ {"abc": 1, "abcd": 2, "Foo": 3} => ["Foo", "abc", "abcd"] jq ´keys´ [42,3,35] => [0,1,2]
has(key)
تابع توکار has بررسی میکند که آیا شیء ورودی دارای کلید دادهشده است، یا اینکه آرایه ورودی در اندیس مشخصشده عنصری دارد یا خیر.
عبارت has($key) تأثیری مشابه بررسی این موضوع دارد که آیا $key عضوی از آرایه بازگرداندهشده توسط keys است یا خیر، اگرچه has سریعتر خواهد بود.
-
jq ´map(has("foo"))´ [{"foo": 42}, {}] => [true, false] jq ´map(has(2))´ [[0,1], ["a","b","c"]] => [false, true]
in
تابع توکار in بررسی میکند که آیا کلید ورودی در شیء دادهشده وجود دارد یا اینکه اندیس ورودی با عنصری در آرایه دادهشده مطابقت دارد یا خیر. این تابع در واقع نسخه معکوسشده has است.
-
jq ´.[] | in({"foo": 42})´ ["foo", "bar"] => true, false jq ´map(in([0,1]))´ [2, 0] => [false, true]
map(f), map_values(f)
برای هر فیلتر f، توابع map(f) و map_values(f) فیلتر f را روی تکتک مقادیر موجود در آرایه یا شیء ورودی، یعنی روی مقادیر حاصل از .[] اعمال میکنند.
در صورت عدم وجود خطا، map(f) همیشه یک آرایه خروجی میدهد، در حالی که map_values(f) در صورت دریافت آرایه، آرایه و در صورت دریافت شیء، یک شیء خروجی میدهد.
هنگامی که ورودی map_values(f) یک شیء است، شیء خروجی دارای همان کلیدهای شیء ورودی است به جز کلیدهایی که مقادیر آنها هنگام ارسال به f هیچ مقداری تولید نمیکنند.
تفاوت کلیدی میان map(f) و map_values(f) در این است که تابع اول صرفاً از تمام مقادیر حاصل از ($x|f) به ازای هر مقدار $x در آرایه یا شیء ورودی یک آرایه تشکیل میدهد، اما map_values(f) تنها از first($x|f) استفاده میکند.
به طور مشخص، برای ورودیهای از نوع شیء، map_values(f) شیء خروجی را با بررسی نوبتی مقدار first(.[$k]|f) به ازای هر کلید $k ورودی میسازد. اگر این عبارت هیچ مقداری تولید نکند، کلید مربوطه حذف خواهد شد؛ در غیر این صورت، شیء خروجی آن مقدار را در کلید $k خواهد داشت.
در اینجا چند مثال برای شفافسازی رفتار map و map_values در زمان اعمال روی آرایهها آورده شده است. این مثالها فرض میکنند که ورودی در همه موارد [1] است:
-
map(.+1) #=> [2] map(., .) #=> [1,1] map(empty) #=> [] map_values(.+1) #=> [2] map_values(., .) #=> [1] map_values(empty) #=> []
عبارت map(f) معادل با [.[] | f] است و map_values(f) معادل با .[] |= f میباشد.
در واقع، این عبارات پیادهسازی خود این توابع هستند.
-
jq ´map(.+1)´ [1,2,3] => [2,3,4] jq ´map_values(.+1)´ {"a": 1, "b": 2, "c": 3} => {"a": 2, "b": 3, "c": 4} jq ´map(., .)´ [1,2] => [1,1,2,2] jq ´map_values(. // empty)´ {"a": null, "b": true, "c": false} => {"b":true}
pick(pathexps)
پروجکشن (تصویر) شیء یا آرایه ورودی را بر اساس توالی مشخصشدهای از عبارات مسیر خروجی میدهد، بهگونهای که اگر p هر یک از این مشخصهها باشد، آنگاه مقدار (. | p) برابر با مقدار (. | pick(pathexps) | p) ارزیابی خواهد شد. برای آرایهها، نباید از اندیسهای منفی و مشخصههای .[m:n] استفاده شود.
-
jq ´pick(.a, .b.c, .x)´ {"a": 1, "b": {"c": 2, "d": 3}, "e": 4} => {"a":1,"b":{"c":2},"x":null} jq ´pick(.[2], .[0], .[0])´ [1,2,3,4] => [1,null,3]
path(path_expression)
نمایشهای آرایهای از عبارت مسیر دادهشده در . را خروجی میدهد. خروجیها آرایههایی از رشتهها (کلیدهای شیء) و/یا اعداد (اندیسهای آرایه) هستند.
عبارات مسیر، عبارات jq مانند .a و همچنین .[] هستند. دو نوع عبارت مسیر وجود دارد: آنهایی که میتوانند تطابق دقیق داشته باشند، و آنهایی که نمیتوانند. برای نمونه، .a.b.c یک عبارت مسیر تطابق دقیق است، در حالی که .a[].b اینطور نیست.
تابع path(exact_path_expression) نمایش آرایهای عبارت مسیر را حتی در صورت عدم وجود در . تولید میکند، مشروط بر اینکه . برابر null، یک آرایه یا یک شیء باشد.
تابع path(pattern) در صورت وجود مسیرها در .، نمایشهای آرایهای مسیرهای منطبق با pattern را تولید میکند.
توجه داشته باشید که عبارات مسیر تفاوتی با عبارات معمولی ندارند. عبارت path(..|select(type=="boolean")) تمام مسیرهای منتهی به مقادیر بولی در . را خروجی میدهد، و فقط همان مسیرها را.
-
jq ´path(.a[0].b)´ null => ["a",0,"b"] jq ´[path(..)]´ {"a":[{"b":1}]} => [[],["a"],["a",0],["a",0,"b"]]
del(path_expression)
تابع توکار del یک کلید و مقدار متناظر با آن را از یک شیء حذف میکند.
-
jq ´del(.foo)´ {"foo": 42, "bar": 9001, "baz": 42} => {"bar": 9001, "baz": 42} jq ´del(.[1, 2])´ ["foo", "bar", "baz"] => ["foo"]
getpath(PATHS)
تابع توکار getpath مقادیر یافتشده در . را در هر یک از مسیرهای موجود در PATHS خروجی میدهد.
-
jq ´getpath(["a","b"])´ null => null jq ´[getpath(["a","b"], ["a","c"])]´ {"a":{"b":0, "c":1}} => [0, 1]
setpath(PATHS; VALUE)
تابع توکار setpath مسیرهای PATHS را در . برابر با VALUE قرار میدهد.
-
jq ´setpath(["a","b"]; 1)´ null => {"a": {"b": 1}} jq ´setpath(["a","b"]; 1)´ {"a":{"b":0}} => {"a": {"b": 1}} jq ´setpath([0,"a"]; 1)´ null => [{"a":1}]
delpaths(PATHS)
تابع توکار delpaths مسیرهای PATHS را در . حذف میکند. مقدار PATHS باید آرایهای از مسیرها باشد، که در آن هر مسیر آرایهای از رشتهها و اعداد است.
-
jq ´delpaths([["a","b"]])´ {"a":{"b":1},"x":{"y":2}} => {"a":{},"x":{"y":2}}
to_entries, from_entries, with_entries(f)
این توابع عمل تبدیل بین یک شیء و یک آرایه از جفتهای کلید-مقدار را انجام میدهند. اگر یک شیء به to_entries ارسال شود، آنگاه به ازای هر ورودی k: v در ورودی، آرایه خروجی شامل {"key": k, "value": v} خواهد بود.
تابع from_entries تبدیل معکوس را انجام میدهد، و with_entries(f) یک میانبر برای to_entries | map(f) | from_entries است که برای انجام عملیاتی روی تمام کلیدها و مقادیر یک شیء مفید است. تابع from_entries کلیدهای "key"، "Key"، "name"، "Name"، "value" و "Value" را میپذیرد.
-
jq ´to_entries´ {"a": 1, "b": 2} => [{"key":"a", "value":1}, {"key":"b", "value":2}] jq ´from_entries´ [{"key":"a", "value":1}, {"key":"b", "value":2}] => {"a": 1, "b": 2} jq ´with_entries(.key |= "KEY_" + .)´ {"a": 1, "b": 2} => {"KEY_a": 1, "KEY_b": 2}
select(boolean_expression)
تابع select(f) در صورتی که f برای آن ورودی مقدار true بازگرداند، ورودی خود را بدون تغییر تولید میکند و در غیر این صورت هیچ خروجی تولید نمیکند.
این تابع برای فیلتر کردن لیستها مفید است: عبارت [1,2,3] | map(select(. >= 2)) مقدار [2,3] را به شما خواهد داد.
-
jq ´map(select(. >= 2))´ [1,5,3,0,7] => [5,3,7] jq ´.[] | select(.id == "second")´ [{"id": "first", "val": 1}, {"id": "second", "val": 2}] => {"id": "second", "val": 2}
arrays, objects, iterables, booleans, numbers, normals, finites, strings, nulls, values, scalars
این توابع توکار، به ترتیب فقط ورودیهایی را انتخاب میکنند که آرایهها، اشیاء، پیمایشپذیرها (آرایهها یا اشیاء)، مقادیر بولی، اعداد، اعداد معمولی (normal numbers)، اعداد متناهی (finite numbers)، رشتهها، null، مقادیر غیر-null، و غیرپیمایشپذیرها باشند.
-
jq ´.[]|numbers´ [[],{},1,"foo",null,true,false] => 1
empty
دستور empty هیچ نتیجهای بازنمیگرداند. مطلقاً هیچ چیز. حتی null هم نه.
گاهی مفید واقع میشود. هر وقت به آن نیاز داشته باشید متوجه خواهید شد :)
-
jq ´1, empty, 2´ null => 1, 2 jq ´[1,2,empty,3]´ null => [1,2,3]
error, error(message)
یک خطا به همراه مقدار ورودی، یا با پیام دادهشده به عنوان آرگومان ایجاد میکند. خطاها را میتوان با try/catch دریافت کرد؛ زیر را ببینید.
-
jq ´try error catch .´ "error message" => "error message" jq ´try error("invalid value: \(.)") catch .´ 42 => "invalid value: 42"
halt
برنامه jq را بدون خروجی دیگری متوقف میکند. برنامه jq با وضعیت خروج 0 خارج خواهد شد.
halt_error, halt_error(exit_code)
برنامه jq را بدون خروجی دیگری متوقف میکند. ورودی به صورت خروجی خام روی stderr چاپ خواهد شد (یعنی رشتهها گیومه دوتایی نخواهند داشت) بدون هیچ آرایهای، حتی یک خط جدید.
کد خروج دادهشده exit_code (با مقدار پیشفرض 5) وضعیت خروج jq خواهد بود.
برای نمونه، "Error: something went wrong\n"|halt_error(1).
$__loc__
یک شیء با کلیدهای "file" و "line" تولید میکند، که نام فایل و شماره خطی که $__loc__ در آن رخ داده است به عنوان مقادیر آنها قرار میگیرند.
-
jq ´try error("\($__loc__)") catch .´ null => "{\"file\":\"<top-level>\",\"line\":1}"
paths, paths(node_filter)
تابع paths مسیرهای منتهی به تمام عناصر موجود در ورودی خود را خروجی میدهد (به جز اینکه لیست خالی که نشاندهنده خود . است را خروجی نمیدهد).
تابع paths(f) مسیرهای منتهی به هر مقداری که f برای آن true باشد را خروجی میدهد. به این معنی که paths(type == "number") مسیرهای تمام مقادیر عددی را خروجی میدهد.
-
jq ´[paths]´ [1,[[],{"a":2}]] => [[0],[1],[1,0],[1,1],[1,1,"a"]] jq ´[paths(type == "number")]´ [1,[[],{"a":2}]] => [[0],[1,1,"a"]]
add, add(generator)
فیلتر add یک آرایه را به عنوان ورودی میگیرد و عناصر آرایه را که با هم جمع شدهاند به عنوان خروجی تولید میکند. این عمل بسته به نوع عناصر آرایه ورودی ممکن است به معنای جمع ریاضی، الحاق رشتهها یا ادغام باشد - قوانین همانند قوانین عملگر + (توضیح دادهشده در بالا) هستند.
اگر ورودی یک آرایه خالی باشد، add مقدار null بازمیگرداند.
فیلتر add(generator) به جای ورودی، روی مولد (generator) دادهشده عمل میکند.
-
jq ´add´ ["a","b","c"] => "abc" jq ´add´ [1, 2, 3] => 6 jq ´add´ [] => null jq ´add(.[].a)´ [{"a":3}, {"a":5}, {"b":6}] => 8
any, any(condition), any(generator; condition)
فیلتر any آرایهای از مقادیر بولی را به عنوان ورودی دریافت میکند، و اگر هر یک از عناصر آرایه true باشد، مقدار true را به عنوان خروجی تولید میکند.
اگر ورودی یک آرایه خالی باشد، any مقدار false بازمیگرداند.
حالت any(condition) شرط دادهشده را روی عناصر آرایه ورودی اعمال میکند.
حالت any(generator; condition) شرط دادهشده را روی تمام خروجیهای مولد دادهشده اعمال میکند.
-
jq ´any´ [true, false] => true jq ´any´ [false, false] => false jq ´any´ [] => false
all, all(condition), all(generator; condition)
فیلتر all آرایهای از مقادیر بولی را به عنوان ورودی دریافت میکند، و اگر تمام عناصر آرایه true باشند، مقدار true را به عنوان خروجی تولید میکند.
حالت all(condition) شرط دادهشده را روی عناصر آرایه ورودی اعمال میکند.
ساختار all(generator; condition) شرط ارائهشده را بر تمام خروجیهای generator ارائهشده اعمال میکند.
اگر ورودی یک آرایه خالی باشد، all مقدار true را برمیگرداند.
-
jq ´all´ [true, false] => false jq ´all´ [true, true] => true jq ´all´ [] => true
flatten, flatten(depth)
فیلتر flatten یک آرایه از آرایههای تودرتو را به عنوان ورودی دریافت میکند و یک آرایه مسطح تولید میکند که در آن تمام آرایههای درون آرایه اصلی به صورت بازگشتی با مقادیر خود جایگزین شدهاند. شما میتوانید آرگومانی به آن ارسال کنید تا مشخص شود چند سطح از تودرتویی مسطح شوند.
flatten(2) شبیه flatten است، اما فقط تا دو سطح عمق پیش میرود.
-
jq ´flatten´ [1, [2], [[3]]] => [1, 2, 3] jq ´flatten(1)´ [1, [2], [[3]]] => [1, 2, [3]] jq ´flatten´ [[]] => [] jq ´flatten´ [{"foo": "bar"}, [{"foo": "baz"}]] => [{"foo": "bar"}, {"foo": "baz"}]
range(upto), range(from; upto), range(from; upto; by)
تابع range بازهای از اعداد را تولید میکند. دستور range(4; 10) تعداد ۶ عدد از ۴ (شامل خود آن) تا ۱۰ (بدون شامل شدن خود آن) تولید میکند. اعداد به صورت خروجیهای جداگانه تولید میشوند. برای دریافت بازه به صورت یک آرایه، از [range(4; 10)] استفاده کنید.
حالت تکآرگومانی، اعداد را از 0 تا عدد دادهشده با گام افزایش ۱ تولید میکند.
حالت دوآرگومانی، اعداد را از from تا upto با گام افزایش ۱ تولید میکند.
حالت سهآرگومانی، اعداد را از from تا upto با گام افزایش by تولید میکند.
-
jq ´range(2; 4)´ null => 2, 3 jq ´[range(2; 4)]´ null => [2,3] jq ´[range(4)]´ null => [0,1,2,3] jq ´[range(0; 10; 3)]´ null => [0,3,6,9] jq ´[range(0; 10; -1)]´ null => [] jq ´[range(0; -5; -1)]´ null => [0,-1,-2,-3,-4]
floor
تابع floor جزء صحیح روبهپایین (floor) ورودی عددی خود را برمیگرداند.
-
jq ´floor´ 3.14159 => 3
sqrt
تابع sqrt ریشه دوم (جذر) ورودی عددی خود را برمیگرداند.
-
jq ´sqrt´ 9 => 3
tonumber
تابع tonumber ورودی خود را به عنوان یک عدد تجزیه میکند. این تابع رشتههای دارای قالببندی صحیح را به معادل عددی آنها تبدیل میکند، اعداد را بدون تغییر باقی میگذارد و برای تمام ورودیهای دیگر خطا میدهد.
-
jq ´.[] | tonumber´ [1, "1"] => 1, 1
toboolean
تابع toboolean ورودی خود را به عنوان یک مقدار بولی تجزیه میکند. این تابع رشتههای دارای قالببندی صحیح را به معادل بولی آنها تبدیل میکند، مقادیر بولی را بدون تغییر باقی میگذارد و برای تمام ورودیهای دیگر خطا میدهد.
-
jq ´.[] | toboolean´ ["true", "false", true, false] => true, false, true, false
tostring
تابع tostring ورودی خود را به عنوان یک رشته چاپ میکند. رشتهها بدون تغییر باقی میمانند و تمام مقادیر دیگر بهصورت JSON کدگذاری (JSON-encoded) میشوند.
-
jq ´.[] | tostring´ [1, "1", [1]] => "1", "1", "[1]"
type
تابع type نوع آرگومان خود را به عنوان یک رشته برمیگرداند که یکی از مقادیر null، boolean، number، string، array یا object است.
-
jq ´map(type)´ [0, false, [], {}, null, "hello"] => ["number", "boolean", "array", "object", "null", "string"]
infinite, nan, isinfinite, isnan, isfinite, isnormal
برخی عملیات حسابی میتوانند مقادیر بینهایت و "غیرعدد" (NaN) تولید کنند. تابع توکار isinfinite اگر ورودی آن بینهایت باشد، مقدار true را برمیگرداند. تابع توکار isnan اگر ورودی آن یک NaN باشد، مقدار true را برمیگرداند. تابع توکار infinite یک مقدار بینهایت مثبت برمیگرداند. تابع توکار nan یک مقدار NaN برمیگرداند. تابع توکار isnormal اگر ورودی آن یک عدد نرمال باشد، مقدار true را برمیگرداند.
توجه داشته باشید که تقسیم بر صفر یک خطا ایجاد میکند.
در حال حاضر بیشتر عملیات حسابی که روی مقادیر بینهایت، NaNها و زیرنرمالها (sub-normals) عمل میکنند، خطایی ایجاد نمیکنند.
-
jq ´.[] | (infinite * .) < 0´ [-1, 1] => true, false jq ´infinite, nan | type´ null => "number", "number"
sort, sort_by(path_expression)
توابع sort ورودی خود را که باید یک آرایه باشد مرتب میکنند. مقادیر به ترتیب زیر مرتب میشوند:
- null
- false
- true
- اعداد
- رشتهها، به ترتیب الفبایی (بر اساس مقدار کدپوئینت یونیکد)
- آرایهها، به ترتیب واژگانی (lexical)
- اشیاء
ترتیب مرتبسازی برای اشیاء کمی پیچیده است: ابتدا با مقایسه مجموعههای کلیدهای آنها (به عنوان آرایههایی به ترتیب مرتبشده) مقایسه میشوند، و اگر کلیدهای آنها برابر باشند، مقادیر کلید به کلید با یکدیگر مقایسه میشوند.
از sort_by میتوان برای مرتبسازی بر اساس یک فیلد خاص از یک شیء، یا با اعمال هر فیلتر jq استفاده کرد. تابع sort_by(f) دو عنصر را با مقایسه نتیجه f روی هر عنصر با هم مقایسه میکند. هنگامی که f چندین مقدار تولید میکند، ابتدا مقادیر اول را مقایسه میکند، و در صورت برابر بودن مقادیر اول، مقادیر دوم را مقایسه میکند و به همین ترتیب ادامه مییابد.
-
jq ´sort´ [8,3,null,6] => [null,3,6,8] jq ´sort_by(.foo)´ [{"foo":4, "bar":10}, {"foo":3, "bar":10}, {"foo":2, "bar":1}] => [{"foo":2, "bar":1}, {"foo":3, "bar":10}, {"foo":4, "bar":10}] jq ´sort_by(.foo, .bar)´ [{"foo":4, "bar":10}, {"foo":3, "bar":20}, {"foo":2, "bar":1}, {"foo":3, "bar":10}] => [{"foo":2, "bar":1}, {"foo":3, "bar":10}, {"foo":3, "bar":20}, {"foo":4, "bar":10}]
group_by(path_expression)
دستور group_by(.foo) یک آرایه را به عنوان ورودی دریافت میکند، عناصری را که دارای فیلد .foo یکسان هستند در آرایههای جداگانه گروهبندی میکند، و تمام این آرایهها را به عنوان عناصر یک آرایه بزرگتر، مرتبشده بر اساس مقدار فیلد .foo، تولید میکند.
هر عبارت jq، نه فقط دسترسی به فیلد، میتواند به جای .foo استفاده شود. ترتیب مرتبسازی مشابه موارد شرح دادهشده در تابع sort در بالا است.
-
jq ´group_by(.foo)´ [{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}] => [[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]
min, max, min_by(path_exp), max_by(path_exp)
کمترین یا بیشترین عنصر آرایه ورودی را پیدا میکند.
توابع min_by(path_exp) و max_by(path_exp) به شما امکان میدهند تا فیلد یا ویژگی خاصی را برای بررسی مشخص کنید؛ برای مثال min_by(.foo) شیء دارای کوچکترین فیلد foo را پیدا میکند.
-
jq ´min´ [5,4,2,7] => 2 jq ´max_by(.foo)´ [{"foo":1, "bar":14}, {"foo":2, "bar":3}] => {"foo":2, "bar":3}
unique, unique_by(path_exp)
تابع unique یک آرایه را به عنوان ورودی دریافت میکند و آرایهای از همان عناصر، به صورت مرتبشده و با حذف موارد تکراری تولید میکند.
تابع unique_by(path_exp) برای هر مقدار بهدستآمده از اعمال آرگومان، تنها یک عنصر را نگه میدارد. میتوانید آن را مانند ایجاد یک آرایه از طریق برداشتن یک عنصر از هر گروه تولیدشده توسط group در نظر بگیرید.
-
jq ´unique´ [1,2,5,3,5,3,1,3] => [1,2,3,5] jq ´unique_by(.foo)´ [{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3}, {"foo": 4, "bar": 5}] => [{"foo": 1, "bar": 2}, {"foo": 4, "bar": 5}] jq ´unique_by(length)´ ["chunky", "bacon", "kitten", "cicada", "asparagus"] => ["bacon", "chunky", "asparagus"]
reverse
این تابع یک آرایه را معکوس میکند.
-
jq ´reverse´ [1,2,3,4] => [4,3,2,1]
contains(element)
فیلتر contains(b) در صورتی مقدار true تولید میکند که b به طور کامل در ورودی گنجانده شده باشد. یک رشته B در یک رشته A گنجانده شده است اگر B زیررشتهای از A باشد. یک آرایه B در یک آرایه A گنجانده شده است اگر تمام عناصر موجود در B در هر یک از عناصر A گنجانده شده باشند. یک شیء B در یک شیء A گنجانده شده است اگر تمام مقادیر موجود در B در مقدار مربوط به همان کلید در A گنجانده شده باشند. سایر انواع در صورتی فرض میشوند که در یکدیگر گنجانده شدهاند که با یکدیگر برابر باشند.
-
jq ´contains("bar")´ "foobar" => true jq ´contains(["baz", "bar"])´ ["foobar", "foobaz", "blarp"] => true jq ´contains(["bazzzzz", "bar"])´ ["foobar", "foobaz", "blarp"] => false jq ´contains({foo: 12, bar: [{barp: 12}]})´ {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]} => true jq ´contains({foo: 12, bar: [{barp: 15}]})´ {"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]} => false
indices(s)
آرایهای شامل اندیسهایی در . که s در آنها رخ داده است را در خروجی تولید میکند. ورودی میتواند یک آرایه باشد، که در این صورت اگر s یک آرایه باشد، اندیسهای خروجی مواردی خواهند بود که تمام عناصر در . با عناصر s مطابقت داشته باشند.
-
jq ´indices(", ")´ "a,b, cd, efg, hijk" => [3,7,12] jq ´indices(1)´ [0,1,2,1,3,1,4] => [1,3,5] jq ´indices([1,2])´ [0,1,2,3,1,4,2,5,1,2,6,7] => [1,8]
index(s), rindex(s)
اندیس اولین (index) یا آخرین (rindex) رخداد s در ورودی را در خروجی تولید میکند.
-
jq ´index(", ")´ "a,b, cd, efg, hijk" => 3 jq ´index(1)´ [0,1,2,1,3,1,4] => 1 jq ´index([1,2])´ [0,1,2,3,1,4,2,5,1,2,6,7] => 1 jq ´rindex(", ")´ "a,b, cd, efg, hijk" => 12 jq ´rindex(1)´ [0,1,2,1,3,1,4] => 5 jq ´rindex([1,2])´ [0,1,2,3,1,4,2,5,1,2,6,7] => 8
inside
فیلتر inside(b) اگر ورودی بهطور کامل درون b قرار داشته باشد، مقدار true تولید میکند. این فیلتر در اصل نسخه معکوس contains است.
-
jq ´inside("foobar")´ "bar" => true jq ´inside(["foobar", "foobaz", "blarp"])´ ["baz", "bar"] => true jq ´inside(["foobar", "foobaz", "blarp"])´ ["bazzzzz", "bar"] => false jq ´inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})´ {"foo": 12, "bar": [{"barp": 12}]} => true jq ´inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})´ {"foo": 12, "bar": [{"barp": 15}]} => false
startswith(str)
اگر . با آرگومان رشتهای دادهشده آغاز شود، مقدار true را در خروجی تولید میکند.
-
jq ´[.[]|startswith("foo")]´ ["fo", "foo", "barfoo", "foobar", "barfoob"] => [false, true, false, true, false]
endswith(str)
اگر . با آرگومان رشتهای دادهشده پایان یابد، مقدار true را در خروجی تولید میکند.
-
jq ´[.[]|endswith("foo")]´ ["foobar", "barfoo"] => [false, true]
combinations, combinations(n)
تمام ترکیبات عناصر آرایههای موجود در آرایه ورودی را در خروجی تولید میکند. اگر آرگومان n داده شود، تمام ترکیبات n بار تکرار آرایه ورودی را در خروجی تولید میکند.
-
jq ´combinations´ [[1,2], [3, 4]] => [1, 3], [1, 4], [2, 3], [2, 4] jq ´combinations(2)´ [0, 1] => [0, 0], [0, 1], [1, 0], [1, 1]
ltrimstr(str)
ورودی خود را در صورتی که با رشته پیشوند دادهشده آغاز شود، پس از حذف آن پیشوند در خروجی تولید میکند.
-
jq ´[.[]|ltrimstr("foo")]´ ["fo", "foo", "barfoo", "foobar", "afoo"] => ["fo","","barfoo","bar","afoo"]
rtrimstr(str)
ورودی خود را در صورتی که با رشته پسوند دادهشده پایان یابد، پس از حذف آن پسوند در خروجی تولید میکند.
-
jq ´[.[]|rtrimstr("foo")]´ ["fo", "foo", "barfoo", "foobar", "foob"] => ["fo","","bar","foobar","foob"]
trimstr(str)
ورودی خود را در صورتی که با رشته دادهشده آغاز یا پایان یابد، پس از حذف آن رشته از هر دو طرف در خروجی تولید میکند.
-
jq ´[.[]|trimstr("foo")]´ ["fo", "foo", "barfoo", "foobarfoo", "foob"] => ["fo","","bar","bar","b"]
trim, ltrim, rtrim
تابع trim فاصلههای خالی ابتدا و انتهای متن را حذف میکند.
تابع ltrim فقط فاصلههای خالی ابتدای متن (سمت چپ) را حذف میکند.
تابع rtrim فقط فاصلههای خالی انتهای متن (سمت راست) را حذف میکند.
نویسههای فاصله خالی همان نویسههای معمول " "، "\n"، "\t"، "\r" و همچنین تمام نویسههای دارای ویژگی فاصله خالی (whitespace) در پایگاهداده نویسههای یونیکد هستند. توجه داشته باشید که آنچه فاصله خالی در نظر گرفته میشود ممکن است در آینده تغییر کند.
-
jq ´trim, ltrim, rtrim´ " abc " => "abc", "abc ", " abc"
explode
یک رشته ورودی را به آرایهای از شمارههای کدپوینت (codepoint) آن رشته تبدیل میکند.
-
jq ´explode´ "foobar" => [102,111,111,98,97,114]
implode
معکوس تابع explode است.
-
jq ´implode´ [65, 66, 67] => "ABC"
split(str)
یک رشته ورودی را بر اساس آرگومان جداکننده تقسیم (split) میکند.
تابع split همچنین میتواند در صورت فراخوانی با دو آرگومان، بر اساس تطابقهای عبارات باقاعده (regex) تفکیک انجام دهد (بخش عبارات باقاعده در زیر را ببینید).
-
jq ´split(", ")´ "a, b,c,d, e, " => ["a","b,c,d","e",""]
join(str)
عناصر آرایه دادهشده به عنوان ورودی را با استفاده از آرگومان به عنوان جداکننده به یکدیگر پیوند میدهد. این تابع معکوس split است؛ یعنی اجرای split("foo") | join("foo") روی هر رشته ورودی، همان رشته ورودی را برمیگرداند.
اعداد و مقادیر بولی در ورودی به رشته تبدیل میشوند. مقادیر null به عنوان رشته خالی در نظر گرفته میشوند. آرایهها و اشیاء در ورودی پشتیبانی نمیشوند.
-
jq ´join(", ")´ ["a","b,c,d","e"] => "a, b,c,d, e" jq ´join(" ")´ ["a",1,2.3,true,null,false] => "a 1 2.3 true false"
ascii_downcase, ascii_upcase
یک کپی از رشته ورودی را همراه با تبدیل نویسههای الفبایی آن (a-z و A-Z) به بزرگی/کوچکی حروف مشخصشده در خروجی ارسال میکند.
-
jq ´ascii_upcase´ "useful but not for é" => "USEFUL BUT NOT FOR é"
while(cond; update)
تابع while(cond; update) به شما امکان میدهد تا زمانی که cond برابر با false شود، یک بهروزرسانی را بهطور مکرر روی . اعمال کنید.
توجه داشته باشید که while(cond; update) در داخل به عنوان یک تابع بازگشتی jq تعریف شده است. فراخوانیهای بازگشتی درون while در صورتی که update حداکثر یک خروجی برای هر ورودی تولید کند، حافظه اضافی مصرف نخواهند کرد. بخش مباحث پیشرفته در زیر را ببینید.
-
jq ´[while(.<100; .*2)]´ 1 => [1,2,4,8,16,32,64]
repeat(exp)
تابع repeat(exp) به شما امکان میدهد تا عبارت exp را بهطور مکرر روی . اعمال کنید تا زمانی که خطایی رخ دهد.
توجه داشته باشید که repeat(exp) در داخل به عنوان یک تابع بازگشتی jq تعریف شده است. فراخوانیهای بازگشتی درون repeat در صورتی که exp حداکثر یک خروجی برای هر ورودی تولید کند، حافظه اضافی مصرف نخواهند کرد. بخش مباحث پیشرفته در زیر را ببینید.
-
jq ´[repeat(.*2, error)?]´ 1 => [2]
until(cond; next)
تابع until(cond; next) به شما اجازه میدهد تا عبارت next را بهطور مکرر، ابتدا روی . و سپس روی خروجی خودش اعمال کنید تا زمانی که cond درست (true) شود. به عنوان مثال، میتوان از این تابع برای پیادهسازی یک تابع فاکتوریل استفاده کرد (به زیر نگاه کنید).
توجه داشته باشید که until(cond; next) بهصورت داخلی به عنوان یک تابع بازگشتی در jq تعریف شده است. در صورتی که next برای هر ورودی حداکثر یک خروجی تولید کند، فراخوانیهای بازگشتی درون until() حافظه اضافی مصرف نخواهند کرد. به مباحث پیشرفته در ادامه مراجعه کنید.
-
jq ´[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]´ 4 => 24
recurse(f), recurse, recurse(f; condition)
تابع recurse(f) به شما امکان میدهد در یک ساختار بازگشتی جستجو کرده و دادههای مورد نظر را از تمام سطوح آن استخراج نمایید. فرض کنید ورودی شما نمایانگر یک فایلسیستم است:
-
{"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(.children[]) | .name
هنگامی که بدون آرگومان فراخوانی شود، recurse معادل recurse(.[]?) خواهد بود.
تابع recurse(f) با recurse(f; true) یکسان است و میتوان آن را بدون نگرانی در مورد عمق بازگشت به کار برد.
مولد recurse(f; condition) با ارسال . آغاز میکند و سپس تا زمانی که مقدار محاسبهشده شرط را برآورده سازد، به نوبت .|f، .|f|f، .|f|f|f، ... را ارسال میکند. برای مثال، جهت تولید تمام اعداد صحیح، حداقل از نظر تئوری، میتوان نوشت recurse(.+1; true).
فراخوانیهای بازگشتی در recurse، هر زمان که f برای هر ورودی حداکثر یک خروجی تولید کند، حافظه اضافی مصرف نخواهند کرد.
-
jq ´recurse(.foo[])´ {"foo":[{"foo": []}, {"foo":[{"foo":[]}]}]} => {"foo":[{"foo":[]},{"foo":[{"foo":[]}]}]}, {"foo":[]}, {"foo":[{"foo":[]}]}, {"foo":[]} jq ´recurse´ {"a":0,"b":[1]} => {"a":0,"b":[1]}, 0, [1], 1 jq ´recurse(. * .; . < 20)´ 2 => 2, 4, 16
walk(f)
تابع walk(f) به صورت بازگشتی f را روی تمام بخشهای موجودیت ورودی اعمال میکند. هنگامی که با یک آرایه مواجه میشود، f ابتدا روی عناصر آن و سپس روی خود آرایه اعمال میگردد؛ زمانی که با یک شیء (object) مواجه میشود، f ابتدا روی تمام مقادیر و سپس روی خود شیء اعمال میشود. در عمل، f معمولاً نوع ورودی خود را بررسی میکند، همانطور که در مثالهای زیر نشان داده شده است. مثال اول کاربرد پردازش عناصر یک آرایه از آرایهها را پیش از پردازش خود آرایه برجسته میکند. مثال دوم نشان میدهد چگونه میتوان تمام کلیدهای اشیاء درون ورودی را برای تغییر بررسی و اعمال کرد.
-
jq ´walk(if type == "array" then sort else . end)´ [[4, 1, 7], [8, 5, 2], [3, 6, 9]] => [[1,4,7],[2,5,8],[3,6,9]] jq ´walk( if type == "object" then with_entries( .key |= sub( "^_+"; "") ) else . end )´ [ { "_a": { "__b": 2 } } ] => [{"a":{"b":2}}]
have_literal_numbers
این تابع توکار در صورتی مقدار true را برمیگرداند که پیکربندی ساخت jq شامل پشتیبانی از حفظ فرمت دقیق لیترالهای عددی ورودی باشد.
have_decnum
این تابع توکار در صورتی مقدار true را برمیگرداند که jq با "decnum" کامپایل شده باشد، که پیادهسازی بکاند عددی فعلی برای حفظ لیترالهای عددی در jq است.
$JQ_BUILD_CONFIGURATION
این متغیر سراسری توکار، پیکربندی ساخت فایل اجرایی jq را نشان میدهد. مقدار آن فرمت خاصی ندارد، اما میتوان انتظار داشت که حداقل شامل آرگومانهای خط فرمان ./configure باشد، و ممکن است در آینده شامل رشتههای نسخه ابزارهای ساخت مورد استفاده نیز بشود.
توجه داشته باشید که این مقدار میتواند در خط فرمان با گزینه --arg و گزینههای مرتبط بازنویسی (override) شود.
$ENV, env
$ENV شیئی است که متغیرهای محیطی را در زمان شروع برنامه jq نشان میدهد.
دستور env شیئی را برمیگرداند که محیط فعلی jq را نمایش میدهد.
در حال حاضر هیچ دستور توکاری برای مقداردهی متغیرهای محیطی وجود ندارد.
-
jq ´$ENV.PAGER´ null => "less" jq ´env.PAGER´ null => "less"
transpose
ترانهاده کردن یک ماتریس که ممکن است دندانهدار (آرایهای از آرایهها با طول نامساوی) باشد. سطرها با مقادیر null پر میشوند تا نتیجه همیشه مستطیلی شکل باشد.
-
jq ´transpose´ [[1], [2,3]] => [[1,2],[null,3]]
bsearch(x)
تابع bsearch(x) یک جستجوی دودویی (binary search) برای یافتن x در آرایه ورودی انجام میدهد. اگر ورودی مرتبشده باشد و شامل x باشد، bsearch(x) اندیس آن را در آرایه برمیگرداند؛ در غیر این صورت، اگر آرایه مرتبشده باشد، مقدار (-1 - ix) را بازمیگرداند که در آن ix نقطه درج است بهطوری که آرایه پس از درج x در ix همچنان مرتب بماند. اگر آرایه مرتب نباشد، bsearch(x) یک عدد صحیح برمیگرداند که احتمالاً کاربردی نخواهد داشت.
-
jq ´bsearch(0)´ [0,1] => 0 jq ´bsearch(0)´ [1,2,3] => -1 jq ´bsearch(4) as $ix | if $ix < 0 then .[-(1+$ix)] = 4 else . end´ [1,2,3] => [1,2,3,4]
String interpolation: \(exp)
درون یک رشته، میتوانید یک عبارت را بعد از یک بکاسلش درون پرانتز قرار دهید. هر آنچه که عبارت برگرداند، در رشته درونیابی (interpolate) خواهد شد.
-
jq ´"The input was \(.), which is one less than \(.+1)"´ 42 => "The input was 42, which is one less than 43"
Convert to/from JSON
دستورات توکار tojson و fromjson به ترتیب مقادیر را به متنهای JSON تبدیل میکنند یا متنهای JSON را به مقادیر تجزیه مینمایند. دستور توکار tojson با tostring تفاوت دارد، زیرا tostring رشتهها را بدون تغییر برمیگرداند، در حالی که tojson رشتهها را به صورت رشتههای کدگذاریشده JSON درمیآورد.
-
jq ´[.[]|tostring]´ [1, "foo", ["foo"]] => ["1","foo","[\"foo\"]"] jq ´[.[]|tojson]´ [1, "foo", ["foo"]] => ["1","\"foo\"","[\"foo\"]"] jq ´[.[]|tojson|fromjson]´ [1, "foo", ["foo"]] => [1,"foo",["foo"]]
Format strings and escaping
ساختار @foo برای قالببندی و اسکیپکردن رشتهها استفاده میشود که برای ساخت URLها، اسناد در زبانهایی مانند HTML یا XML و غیره کاربرد دارد. @foo میتواند به عنوان یک فیلتر مستقل استفاده شود؛ روشهای ممکن اسکیپکردن عبارتند از:
- @text:
- تابع tostring را فراخوانی میکند، برای جزئیات به آن تابع مراجعه کنید.
- @json:
- ورودی را به فرمت JSON سریالسازی میکند.
- @html:
- اسکیپکردن HTML/XML را با نگاشت کاراکترهای <>&´" به موجودیتهای معادل <، >، &، ' و " اعمال میکند.
- @uri:
- کدگذاری درصدی (percent-encoding) را با نگاشت تمام کاراکترهای رزروشده URI به یک توالی %XX اعمال میکند.
- @urid:
- عکس @uri است و رمزگشایی درصدی (percent-decoding) را با نگاشت تمام توالیهای %XX به کاراکترهای URI متناظر آنها اعمال میکند.
- @csv:
- ورودی باید یک آرایه باشد و به عنوان CSV با دابل کوتیشن برای رشتهها رندر میشود و کوتیشنها با تکرار اسکیپ میشوند.
- @tsv:
- ورودی باید یک آرایه باشد و به عنوان TSV (مقادیر جدا شده با تب) رندر میشود. هر آرایه ورودی به عنوان یک خط چاپ خواهد شد. فیلدها با یک نویسه تب (اسکی 0x09) جدا میشوند. کاراکترهای ورودی خط جدید (اسکی 0x0a)، بازگشت به ابتدای سطر (اسکی 0x0d)، تب (اسکی 0x09) و بکاسلش (اسکی 0x5c) به ترتیب به صورت توالیهای اسکیپ \n، \r، \t و \\ در خروجی درج میشوند.
- @sh:
- ورودی به گونهای اسکیپ میشود که برای استفاده در خط فرمان شل POSIX مناسب باشد. اگر ورودی یک آرایه باشد، خروجی رشتههایی جدا شده با فاصله خواهد بود.
- @base64:
- ورودی طبق استاندارد RFC 4648 به base64 تبدیل میشود.
- @base64d:
- عکس @base64 است، ورودی طبق استاندارد RFC 4648 رمزگشایی میشود. نکته\: اگر رشته رمزگشاییشده UTF-8 نباشد، نتایج نامشخص خواهد بود.
این سینتکس میتواند به روشی کاربردی با درونیابی رشتهها ترکیب شود. میتوانید بعد از یک نشانه @foo یک لیترال رشتهای قرار دهید. محتویات لیترال رشتهای اسکیپ نخواهند شد. با این حال، تمام درونیابیهای انجامشده در داخل آن لیترال رشتهای اسکیپ میشوند. برای نمونه،
-
@uri "https://www.google.com/search?q=\(.search)"
برای ورودی {"search":"what is jq?"} خروجی زیر را تولید میکند:
-
"https://www.google.com/search?q=what%20is%20jq%3F"
توجه داشته باشید که اسلشها، علامت سوال و غیره در URL اسکیپ نشدهاند، زیرا بخشی از لیترال رشتهای بودند.
-
jq ´@html´ "This works if x < y" => "This works if x < y" jq ´@sh "echo \(.)"´ "O´Hara´s Ale" => "echo ´O´\\´´Hara´\\´´s Ale´" jq ´@base64´ "This is a message" => "VGhpcyBpcyBhIG1lc3NhZ2U=" jq ´@base64d´ "VGhpcyBpcyBhIG1lc3NhZ2U=" => "This is a message"
Dates
برنامه jq قابلیتهای اولیهای برای مدیریت تاریخ با چندین تابع توکار سطح بالا و سطح پایین فراهم میکند. در تمام موارد، این توابع توکار منحصراً با زمان در قالب UTC کار میکنند.
تابع توکار fromdateiso8601 تاریخوزمانها را در قالب ISO 8601 به تعداد ثانیههای سپریشده از مبدأ یونیکس (1970-01-01T00:00:00Z) تجزیه میکند. تابع توکار todateiso8601 عکس این عمل را انجام میدهد.
تابع توکار fromdate رشتههای تاریخوزمان را تجزیه میکند. در حال حاضر fromdate فقط از رشتههای تاریخوزمان ISO 8601 پشتیبانی میکند، اما در آینده تلاش خواهد کرد رشتههای تاریخوزمان را در قالبهای بیشتری تجزیه کند.
تابع توکار todate یک نام مستعار (alias) برای todateiso8601 است.
تابع توکار now زمان فعلی را بر حسب ثانیههای سپریشده از مبدأ یونیکس در خروجی میدهد.
رابطهای سطح پایین jq به توابع زمانی کتابخانه C نیز ارائه شدهاند: strptime، strftime، strflocaltime، mktime، gmtime و localtime. برای رشتههای قالببندی مورد استفاده در strptime و strftime به مستندات سیستمعامل میزبان خود مراجعه کنید. نکته: این رابطها لزوماً رابطهای پایداری در jq نیستند، به ویژه در مورد قابلیت بومیسازی (localization) آنها.
تابع توکار gmtime تعداد ثانیههای گذشته از مبدأ زمان یونیکس (Unix epoch) را دریافت کرده و یک نمایش «زمان تفکیکشده» از ساعت هماهنگ گرینویچ (GMT) را به صورت آرایهای از اعداد خروجی میدهد که (به این ترتیب) نشاندهنده: سال، ماه (مبتنی بر صفر)، روز ماه (مبتنی بر یک)، ساعت شبانهروز، دقیقه از ساعت، ثانیه از دقیقه، روز هفته و روز سال هستند -- به جز موارد مشخصشده، همگی مبتنی بر یک میباشند. شماره روز هفته ممکن است در برخی سیستمها برای تاریخهای قبل از ۱ مارس ۱۹۰۰، یا بعد از ۳۱ دسامبر ۲۰۹۹ نادرست باشد.
تابع توکار localtime مانند تابع توکار gmtime عمل میکند، اما از تنظیمات منطقه زمانی محلی استفاده مینماید.
تابع توکار mktime نمایشهای «زمان تفکیکشده» خروجی دادهشده توسط gmtime و strptime را مصرف میکند.
تابع توکار strptime(fmt) رشتههای ورودی منطبق با آرگومان fmt را پردازش (parse) میکند. خروجی در قالب نمایش «زمان تفکیکشده» است که توسط mktime دریافت شده و توسط gmtime خروجی داده میشود.
تابع توکار strftime(fmt) یک زمان (GMT) را با قالب مشخصشده قالببندی میکند. تابع strflocaltime نیز همین کار را انجام میدهد، اما از تنظیمات منطقه زمانی محلی استفاده میکند.
رشتههای قالب برای strptime و strftime در مستندات استاندارد کتابخانه C شرح داده شدهاند. رشته قالب برای تاریخ و زمان استاندارد ISO 8601 برابر "%Y-%m-%dT%H:%M:%SZ" است.
دستور jq ممکن است در برخی سیستمها از تمام یا بخشی از این قابلیتهای تاریخ پشتیبانی نکند. به طور خاص، مشخصکنندههای %u و %j برای strptime(fmt) در سیستمعامل macOS پشتیبانی نمیشوند.
-
jq ´fromdate´ "2015-03-05T23:51:47Z" => 1425599507 jq ´strptime("%Y-%m-%dT%H:%M:%SZ")´ "2015-03-05T23:51:47Z" => [2015,2,5,23,51,47,4,63] jq ´strptime("%Y-%m-%dT%H:%M:%SZ")|mktime´ "2015-03-05T23:51:47Z" => 1425599507
عملگرهای سبک SQL (SQL-Style Operators)
دستور jq چند عملگر به سبک SQL ارائه میدهد.
- این تابع توکار شیئی تولید میکند که کلیدهای آن با اعمال عبارت شاخص دادهشده به هر مقدار از جریان مشخصشده محاسبه میشوند.
- این تابع توکار مقادیر را از جریان دادهشده به شاخص مشخص پیوند (join) میدهد. کلیدهای شاخص از طریق اعمال عبارت شاخص دادهشده به هر مقدار از جریان مشخصشده محاسبه میشوند. آرایهای متشکل از مقدار موجود در جریان و مقدار متناظر آن از شاخص، به عبارت پیوند دادهشده ارسال میشود تا هر نتیجه تولید گردد.
- مشابه JOIN($idx; stream; idx_expr; .) است.
- این تابع توکار ورودی . را به شاخص دادهشده پیوند میدهد و عبارت شاخص مشخص را برای محاسبه کلید شاخص بر روی . اعمال میکند. عملیات پیوند همانگونه است که در بالا شرح داده شد.
- این تابع توکار اگر . در جریان دادهشده وجود داشته باشد، مقدار true و در غیر این صورت false را خروجی میدهد.
- این تابع توکار اگر هر مقداری در جریان مبدأ در جریان دوم وجود داشته باشد، مقدار true و در غیر این صورت false را خروجی میدهد.
builtins
فهرستی از تمام توابع توکار را در قالب name/arity برمیگرداند. از آنجا که توابع با نام یکسان اما تعداد آرگومانهای (arity) متفاوت، توابع جداگانهای محسوب میشوند، all/0، all/1 و all/2 همگی در فهرست حضور خواهند داشت.
شرطها و مقایسهها (CONDITIONALS AND COMPARISONS)
==, !=
عبارت ´a == b´ اگر نتایج ارزیابی a و b برابر باشند (یعنی مقادیر JSON معادل را نشان دهند) مقدار ´true´ و در غیر این صورت ´false´ تولید خواهد کرد. به طور خاص، رشتهها هرگز برابر با اعداد در نظر گرفته نمیشوند. در بررسی برابری اشیاء JSON، ترتیب کلیدها بیاهمیت است. اگر با جاوااسکریپت آشنایی دارید، توجه داشته باشید که عملگر == در jq مشابه عملگر برابری دقیق === در جاوااسکریپت است.
عملگر != به معنای «نابرابر» است، و عبارت ´a != b´ مقدار مخالف ´a == b´ را برمیگرداند.
-
jq ´. == false´ null => false jq ´. == {"b": {"d": (4 + 1e-20), "c": 3}, "a":1}´ {"a":1, "b": {"c": 3, "d": 4}} => true jq ´.[] == 1´ [1, 1.0, "1", "banana"] => true, true, false, false
if-then-else-end
ساختار if A then B else C end در صورتی که A مقداری به جز false یا null تولید کند عملکردی مشابه B خواهد داشت، اما در غیر این صورت مانند C رفتار میکند.
ساختار if A then B end همانند if A then B else . end است. یعنی بخش else اختیاری است و در صورت عدم وجود، معادل . خواهد بود. این موضوع برای elif بدون شاخه پایانی else نیز صدق میکند.
بررسی false یا null مفهوم سادهتری از «درستی» (truthiness) نسبت به جاوااسکریپت یا پایتون است، اما به این معناست که گاهی باید شرط مورد نظر خود را با صراحت بیشتری بیان کنید. برای نمونه نمیتوانید خالی بودن یک رشته را با if .name then A else B end بررسی کنید؛ در عوض به عبارتی مانند if .name == "" then A else B end نیاز خواهید داشت.
اگر شرط A چندین نتیجه تولید کند، B یک بار به ازای هر نتیجهای که false یا null نباشد ارزیابی میشود، و C یک بار به ازای هر نتیجه false یا null ارزیابی میگردد.
موارد بیشتری را میتوان با استفاده از نحو elif A then B به یک دستور if اضافه کرد.
-
jq ´if . == 0 then "zero" elif . == 1 then "one" else "many" end´ 2 => "many"
>, >=, <=, <
عملگرهای مقایسهای >، >=، <=، < بررسی میکنند که آیا آرگومان سمت چپ (به ترتیب) بزرگتر از، بزرگتر یا مساوی با، کوچکتر یا مساوی با، یا کوچکتر از آرگومان سمت راست است یا خیر.
ترتیب مرتبسازی همان است که در بالا برای sort شرح داده شد.
-
jq ´. < 5´ 2 => true
and, or, not
دستور jq از عملگرهای بولی معمول and، or و not پشتیبانی میکند. آنها از همان استاندارد درستی عبارات if پیروی میکنند - مقادیر false و null به عنوان «مقادیر نادرست» (false) در نظر گرفته میشوند و هر چیز دیگری «مقدار درست» (true) است.
اگر عملوند یکی از این عملگرها چندین نتیجه تولید کند، خود عملگر نیز به ازای هر ورودی یک نتیجه تولید خواهد کرد.
در واقع not یک تابع توکار است نه یک عملگر، بنابراین به عنوان فیلتری فراخوانی میشود که میتوان دادهها را به آن پایپ کرد و نحو خاصی ندارد؛ مانند .foo and .bar | not.
این سه عملگر تنها مقادیر true و false را تولید میکنند و بنابراین تنها برای عملیات بولی واقعی کاربرد دارند، نه کاربرد رایج در زبانهای پرل/پایتون/روبی به صورت "value_that_may_be_null or default". اگر میخواهید از این شکل از "or" برای انتخاب بین دو مقدار به جای ارزیابی یک شرط استفاده کنید، به عملگر // در زیر مراجعه نمایید.
-
jq ´42 and "a string"´ null => true jq ´(true, false) or false´ null => true, false jq ´(true, true) and (true, false)´ null => true, false, true, false jq ´[true, false | not]´ null => [false, true]
عملگر جایگزین: // (Alternative operator: //)
عملگر // تمام مقادیر سمت چپ خود را که نه false و نه null هستند تولید میکند. اگر سمت چپ مقداری غیر از false یا null تولید نکند، عملگر // تمام مقادیر سمت راست خود را تولید خواهد کرد.
فیلتری به شکل a // b تمام نتایج a را که false یا null نیستند تولید میکند. اگر a هیچ نتیجهای تولید نکند، یا هیچ نتیجهای به جز false یا null نداشته باشد، آنگاه a // b نتایج b را تولید میکند.
این قابلیت برای تعیین مقادیر پیشفرض مفید است: اگر هیچ عنصر .foo در ورودی وجود نداشته باشد، عبارت .foo // 1 به 1 ارزیابی میشود. این رفتار مشابه کاربرد عملگر or در پایتون است (عملگر or در jq منحصراً برای عملیات بولی رزرو شده است).
نکته: عبارت some_generator // defaults_here با some_generator | . // defaults_here یکسان نیست. دومی برای تمام مقادیر غیر false و غیر null سمت چپ مقادیر پیشفرض تولید میکند، در حالی که اولی این کار را نمیکند. قوانین تقدم عملگرها میتواند این موضوع را گیجکننده سازد. برای نمونه، در false, 1 // 2 سمت چپ عملگر // مقدار 1 است، نه false, 1 -- عبارت false, 1 // 2 همانند false, (1 // 2) تجزیه میشود. در (false, null, 1) | . // 42 سمت چپ عملگر // نماد . است که همیشه فقط یک مقدار تولید میکند، در حالی که در (false, null, 1) // 42 سمت چپ یک مولد سه مقداری است و از آنجا که مقداری به جز false و null تولید میکند، مقدار پیشفرض 42 تولید نمیشود.
-
jq ´empty // 42´ null => 42 jq ´.foo // 42´ {"foo": 19} => 19 jq ´.foo // 42´ {} => 42 jq ´(false, null, 1) // 42´ null => 1 jq ´(false, null, 1) | . // 42´ null => 42, 42, 1
try-catch
خطاها را میتوان با استفاده از ساختار try EXP catch EXP به دام انداخت. عبارت اول اجرا میشود و اگر با خطا مواجه شود، عبارت دوم همراه با پیام خطا اجرا میگردد. خروجی مدیریتکننده خطا (handler) در صورت وجود، به گونهای ارسال میشود که گویی خروجی عبارت مورد آزمایش بوده است.
شکل ساده try EXP از empty به عنوان مدیریتکننده استثنا استفاده میکند.
-
jq ´try .a catch ". is not an object"´ true => ". is not an object" jq ´[.[]|try .a]´ [{}, true, {"a":1}] => [null, 1] jq ´try error("some exception") catch .´ true => "some exception"
خروج از ساختارهای کنترلی (Breaking out of control structures)
یکی از کاربردهای سودمند try/catch خروج از ساختارهای کنترلی مانند reduce، foreach، while و غیره است.
برای نمونه:
-
# Repeat an expression until it raises "break" as an # error, then stop repeating without re-raising the error. # But if the error caught is not "break" then re-raise it. try repeat(exp) catch if .=="break" then empty else error
دستور jq دارای نحوی برای برچسبهای لغوی نامگذاریشده جهت "break" یا "go (back) to" است:
-
label $out | ... break $out ...
عبارت break $label_name باعث میشود برنامه به گونهای عمل کند که گویی نزدیکترین label $label_name (در سمت چپ) مقدار empty را تولید کرده است.
ارتباط میان break و label متناظر آن از نوع لغوی (lexical) است: برچسب باید از نقطه break "قابل مشاهده" باشد.
برای نمونه، جهت خروج از یک reduce:
-
label $out | reduce .[] as $item (null; if .==false then break $out else ... end)
برنامه jq زیر یک خطای نحوی (syntax error) ایجاد میکند:
-
break $out
زیرا هیچ برچسب $out قابل مشاهده نیست.
سرکوب خطا / عملگر اختیاری: ? (Error Suppression / Optional Operator: ?)
عملگر ?، که به صورت EXP? استفاده میشود، خلاصهنویسی برای try EXP است.
-
jq ´[.[] | .a?]´ [{}, true, {"a":1}] => [null, 1] jq ´[.[] | tonumber?]´ ["1", "invalid", "3", 4] => [1, 3, 4]
عبارتهای منظم (REGULAR EXPRESSIONS)
دستور jq همانند PHP، TextMate، Sublime Text و غیره از کتابخانه عبارتهای منظم Oniguruma استفاده میکند، بنابراین توضیحات این بخش بر ویژگیهای خاص jq تمرکز دارد.
کتابخانه Oniguruma از چندین نوع (flavor) عبارت منظم پشتیبانی میکند، بنابراین دانستن این نکته حائز اهمیت است که jq از نوع "Perl NG" (پِرل همراه با گروههای نامگذاریشده یا Perl with named groups) استفاده میکند.
فیلترهای عبارت منظم (regex) در jq به گونهای تعریف شدهاند که میتوان با یکی از الگوهای زیر از آنها استفاده کرد:
-
STRING | FILTER(REGEX) STRING | FILTER(REGEX; FLAGS) STRING | FILTER([REGEX]) STRING | FILTER([REGEX, FLAGS])
که در آن:
- مولفههای STRING، REGEX و FLAGS رشتههای jq هستند و مشمول جایگذاری رشتهای (string interpolation) در jq میشوند؛
- مولفه REGEX پس از جایگذاری رشتهای باید یک عبارت منظم معتبر باشد؛
- مولفه FILTER یکی از موارد test، match یا capture است که در ادامه شرح داده شدهاند.
از آنجا که REGEX باید به یک رشته JSON ارزیابی شود، برخی از نویسههای لازم برای ساخت عبارت منظم باید اسکیپ (escape) شوند. برای نمونه، عبارت منظم \s که نشاندهنده یک نویسه فاصله خالی است، به صورت "\\s" نوشته میشود.
مولفه FLAGS رشتهای متشکل از یک یا چند مورد از فلگهای پشتیبانیشده زیر است:
- g - جستجوی سراسری (پیدا کردن تمام تطابقها، نه فقط اولین تطابق)
- i - جستجوی غیرحساس به حروف بزرگ و کوچک
- m - حالت چندخطی (. با خطهای جدید نیز تطابق مییابد)
- n - نادیده گرفتن تطابقهای خالی
- p - فعال بودن همزمان هر دو حالت s و m
- s - حالت تکخطی (^ -> \A, $ -> \Z)
- l - پیدا کردن طولانیترین تطابقهای ممکن
- x - قالب عبارت منظم توسعهیافته (نادیده گرفتن فاصلههای خالی و کامنتها)
برای تطابق با یک فاصله خالی همراه با فلگ x، از \s استفاده کنید، برای نمونه:
-
jq -n ´"a b" | test("a\\sb"; "x")´
توجه داشته باشید که برخی از فلگها را میتوان درون REGEX نیز مشخص کرد، برای نمونه:
-
jq -n ´("test", "TEst", "teST", "TEST") | test("(?i)te(?-i)st")´
به مقادیر روبرو ارزیابی میشود: true, true, false, false.
test(val), test(regex; flags)
همانند match است، اما شیء تطابق را برنمیگرداند، بلکه تنها مقدار true یا false را مبنی بر اینکه عبارت منظم با ورودی تطابق دارد یا خیر بازمیگرداند.
-
jq ´test("foo")´ "foo" => true jq ´.[] | test("a b c # spaces are ignored"; "ix")´ ["xabcd", "ABC"] => true, true
match(val), match(regex; flags)
تابع match به ازای هر تطابقی که پیدا میکند، یک شیء خروجی میدهد. تطابقها دارای فیلدهای زیر هستند:
- offset - آفست بر حسب کدپوینتهای UTF-8 از ابتدای ورودی
- length - طول تطابق بر حسب کدپوینتهای UTF-8
- string - رشتهای که تطابق یافته است
- captures - آرایهای از اشیاء که نشاندهنده گروههای ثبتشده (capturing groups) هستند.
اشیاء گروههای ثبتشده دارای فیلدهای زیر هستند:
- offset - آفست بر حسب کدپوینتهای UTF-8 از ابتدای ورودی
- length - طول این گروه ثبتشده بر حسب کدپوینتهای UTF-8
- string - رشتهای که ثبت (capture) شده است
- name - نام گروه ثبتشده (یا مقدار null در صورتی که بدون نام باشد)
گروههای ثبتشدهای که با چیزی تطابق نیافتهاند، مقدار آفست -1 را برمیگردانند
-
jq ´match("(abc)+"; "g")´ "abc abc" => {"offset": 0, "length": 3, "string": "abc", "captures": [{"offset": 0, "length": 3, "string": "abc", "name": null}]}, {"offset": 4, "length": 3, "string": "abc", "captures": [{"offset": 4, "length": 3, "string": "abc", "name": null}]} jq ´match("foo")´ "foo bar foo" => {"offset": 0, "length": 3, "string": "foo", "captures": []} jq ´match(["foo", "ig"])´ "foo bar FOO" => {"offset": 0, "length": 3, "string": "foo", "captures": []}, {"offset": 8, "length": 3, "string": "FOO", "captures": []} jq ´match("foo (?<bar123>bar)? foo"; "ig")´ "foo bar foo foo foo" => {"offset": 0, "length": 11, "string": "foo bar foo", "captures": [{"offset": 4, "length": 3, "string": "bar", "name": "bar123"}]}, {"offset": 12, "length": 8, "string": "foo foo", "captures": [{"offset": -1, "length": 0, "string": null, "name": "bar123"}]} jq ´[ match("."; "g")] | length´ "abc" => 3
capture(val), capture(regex; flags)
گروههای نامگذاریشده ثبتشده (named captures) را در یک شیء JSON جمعآوری میکند، به طوری که نام هر گروه کلید و رشته تطابقیافته مقدار متناظر آن خواهد بود.
-
jq ´capture("(?<a>[a-z]+)-(?<n>[0-9]+)")´ "xyzzy-14" => { "a": "xyzzy", "n": "14" }
scan(regex), scan(regex; flags)
جریانی از زیررشتههای غیرهمپوشان ورودی را که بر اساس فلگها (در صورت تعیین) با عبارت منظم تطابق دارند منتشر میکند. اگر تطابقی وجود نداشته باشد، جریان خالی خواهد بود. برای گرفتن تمام تطابقها به ازای هر رشته ورودی، از الگوی [ expr ] استفاده کنید، مانند [ scan(regex) ]. اگر عبارت منظم شامل گروههای ثبتکننده (capturing groups) باشد، فیلتر جریانی از آرایهها را منتشر میکند که هر آرایه شامل رشتههای ثبتشده است.
-
jq ´scan("c")´ "abcdefabc" => "c", "c" jq ´scan("(a+)(b+)")´ "abaabbaaabbb" => ["a","b"], ["aa","bb"], ["aaa","bbb"]
split(regex; flags)
یک رشته ورودی را بر اساس هر تطابق عبارت منظم تکهتکه (split) میکند.
برای حفظ سازگاری با نسخههای پیشین، هنگامی که با یک آرگومان فراخوانی شود، split بر اساس یک رشته عمل جداسازی را انجام میدهد، نه بر اساس یک عبارت منظم.
-
jq ´split(", *"; null)´ "ab,cd, ef" => ["ab","cd","ef"]
splits(regex), splits(regex; flags)
این فیلترها همان نتایج همتایان split خود را ارائه میدهند، اما به جای آرایه، به صورت یک جریان (stream) خروجی تولید میکنند.
-
jq ´splits(", *")´ "ab,cd, ef, gh" => "ab", "cd", "ef", "gh" jq ´splits(",? *"; "n")´ "ab,cd ef, gh" => "ab", "cd", "ef", "gh"
sub(regex; tostring), sub(regex; tostring; flags)
رشتهای را تولید میکند که از جایگزینی اولین تطابق عبارت منظم در رشته ورودی با tostring (پس از جایگذاری) حاصل میشود. مقدار tostring باید یک رشته jq یا جریانی از چنین رشتههایی باشد که هر کدام میتوانند شامل ارجاع به گروههای ثبتشده نامگذاریشده باشند. گروههای ثبتشده نامگذاریشده در عمل به صورت یک شیء JSON (همانطور که توسط capture ساخته میشود) به tostring ارائه میشوند، بنابراین ارجاع به یک متغیر ثبتشده به نام "x" به شکل "\(.x)" خواهد بود.
-
jq ´sub("[^a-z]*(?<x>[a-z]+)"; "Z\(.x)"; "g")´ "123abc456def" => "ZabcZdef" jq ´[sub("(?<a>.)"; "\(.a|ascii_upcase)", "\(.a|ascii_downcase)")]´ "aB" => ["AB","aB"]
gsub(regex; tostring), gsub(regex; tostring; flags)
تابع gsub همانند sub است، اما تمام رخدادهای غیرهمپوشان عبارت منظم پس از جایگذاری با tostring جایگزین میشوند. اگر آرگومان دوم جریانی از رشتههای jq باشد، آنگاه gsub جریانی متناظر از رشتههای JSON تولید خواهد کرد.
-
jq ´gsub("(?<x>.)[^a]*"; "+\(.x)-")´ "Abcabc" => "+A-+a-" jq ´[gsub("p"; "a", "b")]´ "p" => ["a","b"]
ویژگیهای پیشرفته (ADVANCED FEATURES)
متغیرها در اکثر زبانهای برنامهنویسی یک ضرورت مطلق هستند، اما در jq به یک «ویژگی پیشرفته» تنزل یافتهاند.
در بیشتر زبانها، متغیرها تنها ابزار انتقال دادهها هستند. اگر مقداری را محاسبه کنید و بخواهید بیش از یک بار از آن استفاده نمایید، باید آن را در یک متغیر ذخیره کنید. برای ارسال یک مقدار به بخش دیگری از برنامه، لازم است آن بخش از برنامه متغیری (به عنوان پارامتر تابع، عضو شیء یا هر چیز دیگر) تعریف کند تا دادهها در آن قرار گیرند.
همچنین در jq امکان تعریف توابع وجود دارد، هرچند بزرگترین کاربرد این ویژگی تعریف کتابخانه استاندارد jq است (بسیاری از توابع jq مانند map و select در واقع به زبان jq نوشته شدهاند).
دستور jq عملگرهای کاهش (reduction operators) دارد که بسیار قدرتمند اما تا حدی پیچیدهاند. باز هم این موارد عمدتاً به صورت داخلی برای تعریف بخشهای مفیدی از کتابخانه استاندارد jq به کار میروند.
شاید در ابتدا واضح نباشد، اما تمام ساختار jq بر پایه مولدها (تولیدکنندهها یا generators، بله همانطور که اغلب در سایر زبانها یافت میشود) بنا شده است. ابزارهایی برای کمک به کار با مولدها فراهم شده است.
پشتیبانی حداقلی از I/O (ورودی/خروجی، علاوه بر خواندن JSON از ورودی استاندارد و نوشتن JSON در خروجی استاندارد) در دسترس است.
در نهایت، یک سیستم ماژول/کتابخانه نیز وجود دارد.
عملگر انتساب متغیر / اتصال نمادین: ... as $identifier | ...
در jq تمام فیلترها دارای ورودی و خروجی هستند، بنابراین نیازی به سیمکشی یا انتقال دستی داده برای فرستادن یک مقدار از یک بخش برنامه به بخش بعدی نیست. بسیاری از عبارتها، برای نمونه a + b، ورودی خود را به دو زیرعبارت مجزا ارسال میکنند (در اینجا به هر دو a و b یک ورودی یکسان داده میشود)، بنابراین معمولاً برای دو بار استفاده از یک مقدار، نیازی به متغیرها نیست.
برای نمونه، محاسبه مقدار میانگین یک آرایه از اعداد در اکثر زبانها به چند متغیر نیاز دارد - حداقل یکی برای نگهداری آرایه، شاید یکی برای هر عنصر یا برای شمارنده حلقه. در jq این کار صرفاً با add / length انجام میشود - عبارت add آرایه را دریافت کرده و مجموع آن را تولید میکند، و عبارت length آرایه را دریافت کرده و طول آن را برمیگرداند.
بنابراین، معمولاً در jq برای حل بیشتر مسائل روشی تمیزتر از تعریف متغیرها وجود دارد. با این حال گاهی اوقات متغیرها کار را آسانتر میکنند، بنابراین jq به شما امکان میدهد متغیرها را با استفاده از expression as $variable تعریف کنید. همه نامهای متغیرها با $ آغاز میشوند. در اینجا نسخه کمی ناخوشایندتر از مثال میانگینگیری آرایه آورده شده است:
-
length as $array_length | add / $array_length
برای یافتن وضعیتی که در آن استفاده از متغیرها واقعاً کار ما را آسانتر کند، به یک مسئله پیچیدهتر نیاز خواهیم داشت.
فرض کنید آرایهای از پستهای وبلاگ با فیلدهای "author" و "title" داریم، و شیء دیگری که برای نگاشت نامهای کاربری نویسندگان به نامهای واقعی آنها استفاده میشود. ورودی ما به این صورت است:
-
{"posts": [{"title": "First post", "author": "anon"}, {"title": "A well-written article", "author": "person1"}], "realnames": {"anon": "Anonymous Coward", "person1": "Person McPherson"}}
میخواهیم پستها را بهگونهای تولید کنیم که فیلد نویسنده (author) حاوی نام واقعی باشد، مانند:
-
{"title": "First post", "author": "Anonymous Coward"} {"title": "A well-written article", "author": "Person McPherson"}
از یک متغیر با نام $names برای ذخیره شیء realnames استفاده میکنیم تا بعداً هنگام جستجوی نام کاربری نویسندگان بتوانیم به آن ارجاع دهیم:
-
.realnames as $names | .posts[] | {title, author: $names[.author]}
عبارت exp as $x | ... به این معنی است: به ازای هر مقدار حاصل از عبارت exp، بقیه پایپلاین را با کل ورودی اصلی اجرا کن در حالی که مقدار $x برابر با آن مقدار تنظیم شده است. بنابراین as به نوعی شبیه یک حلقه foreach عمل میکند.
همانطور که {foo} روشی آسان برای نوشتن {foo: .foo} است، {$foo} نیز روشی آسان برای نوشتن {foo: $foo} محسوب میشود.
میتوان با ارائه الگویی که با ساختار ورودی مطابقت دارد، چندین متغیر را با استفاده از یک عبارت as واحد تعریف کرد (این کار به عنوان «ساختارشکنی» یا destructuring شناخته میشود):
-
. as {realnames: $names, posts: [$first, $second]} | ...
تعریف متغیرها در الگوهای آرایهای (مانند . as [$first, $second]) به ترتیب از عنصر موجود در اندیس صفر به بالا به عناصر آرایه متصل (bind) میشوند. هنگامی که مقداری در آن اندیس برای عنصر الگوی آرایه وجود نداشته باشد، مقدار null به آن متغیر متصل میشود.
دامنه دید متغیرها شامل باقیمانده عبارتی است که آنها را تعریف میکند؛ بنابراین:
-
.realnames as $names | (.posts[] | {title, author: $names[.author]})
کار خواهد کرد، اما:
-
(.realnames as $names | .posts[]) | {title, author: $names[.author]}
کار نخواهد کرد.
از دید نظریهپردازان زبانهای برنامهنویسی، دقیقتر است بگوییم متغیرهای jq پیوندهایی با دامنه لغوی (lexically-scoped bindings) هستند. به ویژه، هیچ راهی برای تغییر مقدار یک پیوند وجود ندارد؛ فقط میتوان پیوند جدیدی با همان نام تعریف کرد که در جایی که پیوند قبلی قرار داشت قابل مشاهده نخواهد بود.
-
jq ´.bar as $x | .foo | . + $x´ {"foo":10, "bar":200} => 210 jq ´. as $i|[(.*2|. as $i| $i), $i]´ 5 => [10,5] jq ´. as [$a, $b, {c: $c}] | $a + $b + $c´ [2, 3, {"c": 4, "d": 5}] => 9 jq ´.[] as [$a, $b] | {a: $a, b: $b}´ [[0], [0, 1], [2, 1, 0]] => {"a":0,"b":null}, {"a":0,"b":1}, {"a":2,"b":1}
Destructuring Alternative Operator: ?//
عملگر جایگزین ساختارشکنی، سازوکاری موجز برای ساختارشکنی ورودیای که میتواند یکی از چندین شکل ممکن را داشته باشد، فراهم میکند.
فرض کنید یک API داریم که فهرستی از منابع و رویدادهای مرتبط با آنها را برمیگرداند، و میخواهیم user_id و برچسب زمانی (timestamp) اولین رویداد را برای هر منبع به دست آوریم. این API (که به شکلی ناشیانه از XML تبدیل شده است) تنها در صورتی رویدادها را درون یک آرایه قرار میدهد که آن منبع دارای چندین رویداد باشد:
-
{"resources": [{"id": 1, "kind": "widget", "events": {"action": "create", "user_id": 1, "ts": 13}}, {"id": 2, "kind": "widget", "events": [{"action": "create", "user_id": 1, "ts": 14}, {"action": "destroy", "user_id": 1, "ts": 15}]}]}
میتوانیم از عملگر جایگزین ساختارشکنی برای مدیریت ساده این تغییر ساختاری استفاده کنیم:
-
.resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$user_id, $ts}]} | {$user_id, $kind, $id, $ts}
یا اگر مطمئن نیستیم ورودی آرایهای از مقادیر است یا یک شیء:
-
.[] as [$id, $kind, $user_id, $ts] ?// {$id, $kind, $user_id, $ts} | ...
نیازی نیست هر جایگزین تمام متغیرهای مشابه را تعریف کند، اما تمام متغیرهای نامگذاریشده در عبارت بعدی در دسترس خواهند بود. متغیرهایی که در جایگزین موفق تطبیق داده نشوند، مقدار null خواهند داشت:
-
.resources[] as {$id, $kind, events: {$user_id, $ts}} ?// {$id, $kind, events: [{$first_user_id, $first_ts}]} | {$user_id, $first_user_id, $kind, $id, $ts, $first_ts}
علاوه بر این، اگر عبارت بعدی خطایی بازگرداند، عملگر جایگزین تلاش میکند اتصال بعدی را امتحان کند. خطاهایی که در طول آخرین جایگزین رخ میدهند، عبور داده میشوند.
-
[[3]] | .[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end jq ´.[] as {$a, $b, c: {$d, $e}} ?// {$a, $b, c: [{$d, $e}]} | {$a, $b, $d, $e}´ [{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}] => {"a":1,"b":2,"d":3,"e":4}, {"a":1,"b":2,"d":3,"e":4} jq ´.[] as {$a, $b, c: {$d}} ?// {$a, $b, c: [{$e}]} | {$a, $b, $d, $e}´ [{"a": 1, "b": 2, "c": {"d": 3, "e": 4}}, {"a": 1, "b": 2, "c": [{"d": 3, "e": 4}]}] => {"a":1,"b":2,"d":3,"e":null}, {"a":1,"b":2,"d":null,"e":4} jq ´.[] as [$a] ?// [$b] | if $a != null then error("err: \($a)") else {$a,$b} end´ [[3]] => {"a":null,"b":3}
Defining Functions
میتوانید با استفاده از نحو "def" برای یک فیلتر نام تعیین کنید:
-
def increment: . + 1;
از آن پس، increment دقیقاً مانند یک تابع توکار به عنوان فیلتر قابل استفاده است (در واقع، بسیاری از توابع توکار به همین شکل تعریف شدهاند). یک تابع میتواند آرگومانهایی بپذیرد:
-
def map(f): [.[] | f];
آرگومانها به عنوان فیلترها (توابع بدون آرگومان) ارسال میشوند، نه به عنوان مقادیر. یک آرگومان یکسان میتواند چندین بار با ورودیهای متفاوت مورد ارجاع قرار گیرد (در اینجا f برای هر عنصر آرایه ورودی اجرا میشود). آرگومانهای یک تابع بیشتر مانند کالبکها (callbacks) عمل میکنند تا آرگومانهای مقداری. درک این نکته حائز اهمیت است؛ به عنوان مثال:
-
def foo(f): f|f; 5|foo(.*2)
نتیجه برابر با 20 خواهد بود زیرا f همان .*2 است، و در طول اولین فراخوانی f مقدار . برابر با 5 خواهد بود و بار دوم برابر با 10 (5 * 2)، بنابراین نتیجه 20 خواهد شد. آرگومانهای توابع فیلتر هستند و فیلترها هنگام فراخوانی انتظار یک ورودی را دارند.
اگر برای تعریف توابع ساده رفتار آرگومان مقداری را میخواهید، میتوانید از یک متغیر استفاده کنید:
-
def addvalue(f): f as $f | map(. + $f);
یا از شکل کوتاهشده استفاده کنید:
-
def addvalue($f): ...;
با هر یک از این دو تعریف، addvalue(.foo) فیلد .foo ورودی جاری را به هر عنصر آرایه اضافه میکند. توجه داشته باشید که فراخوانی addvalue(.[]) باعث میشود بخش map(. + $f) به ازای هر مقدار در مقدار . در محل فراخوانی ارزیابی شود.
تعاریف متعدد با استفاده از نام تابع یکسان مجاز است. هر تعریف مجدد جایگزین تعریف قبلی با همان تعداد آرگومان تابع میشود، اما فقط برای ارجاعات از توابع (یا برنامه اصلی) پس از تعریف مجدد اعمال میگردد. همچنین بخش زیر را در مورد دامنه دید (scoping) ببینید.
-
jq ´def addvalue(f): . + [f]; map(addvalue(.[0]))´ [[1,2],[10,20]] => [[1,2,1], [10,20,10]] jq ´def addvalue(f): f as $x | map(. + $x); addvalue(.[0])´ [[1,2],[10,20]] => [[1,2,1,2], [10,20,1,2]]
Scoping
در jq دو نوع نماد وجود دارد: اتصالات مقداری (یا همان «متغیرها») و توابع. هر دو دارای دامنه لغوی هستند، بهطوریکه عبارتها تنها میتوانند به نمادهایی ارجاع دهند که «در سمت چپ» آنها تعریف شده باشند. تنها استثنای این قاعده این است که توابع میتوانند به خودشان ارجاع دهند تا امکان ایجاد توابع بازگشتی فراهم شود.
برای مثال، در عبارت روبرو اتصالی وجود دارد که «در سمت راست» آن قابل مشاهده است، ... | .*3 as $times_three | [. + $times_three] | ...، اما «در سمت چپ» قابل مشاهده نیست. اکنون این عبارت را در نظر بگیرید، ... | (.*3 as $times_three | [. + $times_three]) | ...: در اینجا اتصال $times_three بعد از پرانتز بسته قابل مشاهده نیست.
isempty(exp)
اگر exp هیچ خروجیای تولید نکند مقدار true و در غیر این صورت false برمیگرداند.
-
jq ´isempty(empty)´ null => true jq ´isempty(.[])´ [] => true jq ´isempty(.[])´ [1,2,3] => false
limit(n; expr)
تابع limit حداکثر n خروجی را از expr استخراج میکند.
-
jq ´[limit(3; .[])]´ [0,1,2,3,4,5,6,7,8,9] => [0,1,2]
skip(n; expr)
تابع skip از اولین n خروجی expr صرفنظر میکند.
-
jq ´[skip(3; .[])]´ [0,1,2,3,4,5,6,7,8,9] => [3,4,5,6,7,8,9]
first(expr), last(expr), nth(n; expr)
توابع first(expr) و last(expr) به ترتیب اولین و آخرین مقادیر را از expr استخراج میکنند.
تابع nth(n; expr) مقدار n-اُم خروجی داده شده توسط expr را استخراج میکند. توجه داشته باشید که nth(n; expr) از مقادیر منفی n پشتیبانی نمیکند.
-
jq ´[first(range(.)), last(range(.)), nth(5; range(.))]´ 10 => [0,9,5] jq ´[first(empty), last(empty), nth(5; empty)]´ null => []
first, last, nth(n)
توابع first و last اولین و آخرین مقادیر را از هر آرایهای در . استخراج میکنند.
تابع nth(n) مقدار n-اُم هر آرایهای را در . استخراج میکند.
-
jq ´[range(.)]|[first, last, nth(5)]´ 10 => [0,9,5]
reduce
دستور نحوی reduce به شما امکان میدهد تا تمام نتایج یک عبارت را با انباشتن آنها در یک پاسخ واحد ترکیب کنید. ساختار آن به صورت reduce EXP as $var (INIT; UPDATE) است. به عنوان مثال، [1,2,3] را به این عبارت ارسال میکنیم:
-
reduce .[] as $item (0; . + $item)
به ازای هر نتیجهای که .[] تولید میکند، . + $item برای انباشتن یک مجموع در حال اجرا با شروع از 0 به عنوان مقدار ورودی اجرا میشود. در این مثال، .[] نتایج 1، 2 و 3 را تولید میکند، بنابراین اثر آن مشابه اجرای چیزی شبیه به این است:
-
0 | 1 as $item | . + $item | 2 as $item | . + $item | 3 as $item | . + $item jq ´reduce .[] as $item (0; . + $item)´ [1,2,3,4,5] => 15 jq ´reduce .[] as [$i,$j] (0; . + $i * $j)´ [[1,2],[3,4],[5,6]] => 44 jq ´reduce .[] as {$x,$y} (null; .x += $x | .y += [$y])´ [{"x":"a","y":1},{"x":"b","y":2},{"x":"c","y":3}] => {"x":"abc","y":[1,2,3]}
foreach
دستور نحوی foreach مشابه reduce است، اما با این هدف طراحی شده که امکان ساخت limit و کاهندههایی (reducers) که نتایج میانی تولید میکنند را فراهم کند.
ساختار آن به صورت foreach EXP as $var (INIT; UPDATE; EXTRACT) است. به عنوان مثال، [1,2,3] را به این عبارت ارسال میکنیم:
-
foreach .[] as $item (0; . + $item; [$item, . * 2])
مانند دستور نحوی reduce، عبارت . + $item به ازای هر نتیجهای که .[] تولید میکند اجرا میشود، اما [$item, . * 2] به ازای هر یک از مقادیر میانی اجرا میگردد. در این مثال، از آنجا که مقادیر میانی 1، 3 و 6 هستند، عبارت foreach مقادیر [1,2]، [2,6] و [3,12] را تولید میکند. بنابراین اثر آن مشابه اجرای چیزی شبیه به این است:
-
0 | 1 as $item | . + $item | [$item, . * 2], 2 as $item | . + $item | [$item, . * 2], 3 as $item | . + $item | [$item, . * 2]
هنگامی که EXTRACT حذف شود، فیلتر همانی (identity) به کار میرود. یعنی مقادیر میانی را همانگونه که هستند در خروجی قرار میدهد.
-
jq ´foreach .[] as $item (0; . + $item)´ [1,2,3,4,5] => 1, 3, 6, 10, 15 jq ´foreach .[] as $item (0; . + $item; [$item, . * 2])´ [1,2,3,4,5] => [1,2], [2,6], [3,12], [4,20], [5,30] jq ´foreach .[] as $item (0; . + 1; {index: ., $item})´ ["foo", "bar", "baz"] => {"index":1,"item":"foo"}, {"index":2,"item":"bar"}, {"index":3,"item":"baz"}
بازگشت (Recursion)
همانطور که در بالا شرح داده شد، recurse از بازگشت استفاده میکند، و هر تابع jq میتواند بازگشتی باشد. تابع توکار while نیز بر مبنای بازگشت پیادهسازی شده است.
فراخوانیهای دمدستی (Tail calls) زمانی بهینهسازی میشوند که عبارت سمت چپ فراخوانی بازگشتی، آخرین مقدار خود را خروجی دهد. در عمل، این بدان معناست که عبارت سمت چپ فراخوانی بازگشتی نباید بیش از یک خروجی به ازای هر ورودی تولید کند.
برای مثال:
-
def recurse(f): def r: ., (f | select(. != null) | r); r; def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; def repeat(exp): def _repeat: exp, _repeat; _repeat;
تولیدکنندهها و تکرارکنندهها (Generators and iterators)
برخی عملگرها و توابع jq در واقع مولد (generator) هستند، به این صورت که میتوانند صفر، یک یا چند مقدار را به ازای هر ورودی تولید کنند، درست مانند آنچه ممکن است در زبانهای برنامهنویسی دیگری که مولد دارند انتظار داشته باشید. برای نمونه، .[] تمام مقادیر ورودی خود را (که باید یک آرایه یا شیء باشد) تولید میکند، range(0; 10) اعداد صحیح بین 0 و 10 را تولید میکند و غیره.
حتی عملگر کاما نیز یک مولد است، و ابتدا مقادیر تولید شده توسط عبارت سمت چپ کاما را تولید میکند، سپس مقادیر تولید شده توسط عبارت سمت راست کاما را تولید مینماید.
تابع توکار empty مولدی است که صفر خروجی تولید میکند. تابع توکار empty به عبارت مولد پیشین برمیگردد (backtrack میکند).
تمام توابع jq میتوانند صرفاً با استفاده از مولدهای توکار به مولد تبدیل شوند. همچنین میتوان تنها با استفاده از بازگشت و عملگر کاما مولدهای جدیدی ساخت. اگر فراخوانیهای بازگشتی «در موقعیت دم» (in tail position) باشند، مولد کارآمد خواهد بود. در مثال زیر فراخوانی بازگشتی توسط _range به خودش در موقعیت دم قرار دارد. این مثال سه مبحث پیشرفته را نشان میدهد: بازگشت دم (tail recursion)، ساخت مولد و زیرتوابع (sub-functions).
-
jq ´def range(init; upto; by): def _range: if (by > 0 and . < upto) or (by < 0 and . > upto) then ., ((.+by)|_range) else empty end; if init == upto then empty elif by == 0 then init else init|_range end; range(0; 10; 3)´ null => 0, 3, 6, 9 jq ´def while(cond; update): def _while: if cond then ., (update | _while) else empty end; _while; [while(.<100; .*2)]´ 1 => [1,2,4,8,16,32,64]
ریاضیات (MATH)
دستور jq در حال حاضر تنها از اعداد ممیز شناور با دقت مضاعف IEEE754 (۶۴ بیتی) پشتیبانی میکند.
علاوه بر عملگرهای حسابی ساده مانند +، jq اکثر توابع ریاضی استاندارد برگرفته از کتابخانه ریاضی C را نیز در خود دارد. توابع ریاضی C که یک آرگومان ورودی دریافت میکنند (مانند sin()) به صورت توابع بدون آرگومان در jq در دسترس هستند. توابع ریاضی C که دو آرگومان ورودی دریافت میکنند (مانند pow()) به عنوان توابع دوآرگومانی در jq در دسترس هستند که . را نادیده میگیرند. توابع ریاضی C که سه آرگومان ورودی دریافت میکنند به صورت توابع سهآرگومانی در jq در دسترس هستند که . را نادیده میگیرند.
در دسترس بودن توابع ریاضی استاندارد به موجود بودن توابع ریاضی متناظر در سیستمعامل و کتابخانه ریاضی C شما بستگی دارد. توابع ریاضی غیرقابل دسترس تعریف خواهند شد اما هنگام اجرا خطا صادر میکنند.
توابع ریاضی C تکورودی: acos acosh asin asinh atan atanh cbrt ceil cos cosh erf erfc exp exp10 exp2 expm1 fabs floor gamma j0 j1 lgamma log log10 log1p log2 logb nearbyint rint round significand sin sinh sqrt tan tanh tgamma trunc y0 y1.
توابع ریاضی C دوورودی: atan2 copysign drem fdim fmax fmin fmod frexp hypot jn ldexp modf nextafter nexttoward pow remainder scalb scalbln yn.
توابع ریاضی C سهورودی: fma.
برای اطلاعات بیشتر درباره هر یک از این توابع به راهنمای سیستم خود مراجعه کنید.
ورودی/خروجی (I/O)
در حال حاضر jq پشتیبانی حداقلی از ورودی/خروجی (I/O) دارد، که عمدتاً به شکل کنترل بر زمان خوانده شدن ورودیهاست. دو تابع توکار برای این منظور ارائه شده است: input و inputs، که از همان منابعی میخوانند که خود jq میخواند (مانند stdin، فایلهای مشخصشده در خط فرمان). این دو تابع توکار و عملیات خواندن خود jq میتوانند به صورت متناوب با یکدیگر اجرا شوند. آنها معمولاً در ترکیب با گزینه ورودی پوچ -n استفاده میشوند تا از خوانده شدن ضمنی یک ورودی جلوگیری کنند.
دو تابع توکار قابلیتهای حداقلی خروجی را فراهم میکنند: debug و stderr. (به یاد داشته باشید که مقادیر خروجی یک برنامه jq همیشه به عنوان متنهای JSON در stdout خروجی داده میشوند.) تابع توکار debug میتواند رفتاری مختص برنامه داشته باشد، مانند فایلهای اجرایی که از C API کتابخانه libjq استفاده میکنند اما خود برنامه اجرایی jq نیستند. تابع توکار stderr ورودی خود را در حالت خام بدون هیچ تزئین اضافی، حتی بدون خط جدید، در stderr خروجی میدهد.
اکثر توابع توکار jq دارای شفافیت ارجاعی (referentially transparent) هستند و هنگامی که روی ورودیهای ثابت اعمال شوند، جریانهایی از مقادیر ثابت و تکرارپذیر تولید میکنند. این موضوع در مورد توابع توکار I/O صدق نمیکند.
input
یک ورودی جدید را خروجی میدهد.
توجه داشته باشید که هنگام استفاده از input عموماً لازم است jq را با گزینه خط فرمان -n فراخوانی کنید، در غیر این صورت نخستین موجودیت از دست خواهد رفت.
-
echo 1 2 3 4 | jq ´[., input]´ # [1,2] [3,4]
inputs
تمام ورودیهای باقیمانده را یکی پس از دیگری خروجی میدهد.
این تابع در درجه اول برای عملیات کاهش و تجمیع (reductions) روی ورودیهای یک برنامه مفید است. توجه داشته باشید که هنگام استفاده از inputs عموماً لازم است jq را با گزینه خط فرمان -n فراخوانی کنید، در غیر این صورت نخستین موجودیت از دست خواهد رفت.
-
echo 1 2 3 | jq -n ´reduce inputs as $i (0; . + $i)´ # 6
debug, debug(msgs)
این دو فیلتر شبیه . هستند اما به عنوان یک اثر جانبی، یک یا چند پیام در stderr تولید میکنند.
پیام تولید شده توسط فیلتر debug دارای این ساختار است:
-
["DEBUG:",<input-value>]
که در آن <input-value> نمایش فشردهای از مقدار ورودی است. این قالب ممکن است در آینده تغییر کند.
فیلتر debug(msgs) به صورت (msgs | debug | empty), . تعریف شده است و از این رو انعطافپذیری بالایی در محتوای پیام فراهم میکند، در حالی که امکان ایجاد دستورات دیباگ چندخطی را نیز میدهد.
برای مثال، عبارت:
-
1 as $x | 2 | debug("Entering function foo with $x == \($x)", .) | (.+1)
مقدار 3 را تولید میکند اما دو خط زیر در stderr نوشته خواهد شد:
-
["DEBUG:","Entering function foo with $x == 1"] ["DEBUG:",2]
stderr
ورودی خود را در حالت خام و فشرده بدون هیچ تزئین اضافی، حتی بدون خط جدید، در stderr چاپ میکند.
input_filename
نام فایلی را که ورودی آن در حال حاضر فیلتر میشود برمیگرداند. توجه داشته باشید که این تابع به خوبی کار نخواهد کرد مگر اینکه jq در یک لوکال UTF-8 اجرا شود.
input_line_number
شماره خط ورودیای را که در حال حاضر فیلتر میشود برمیگرداند.
جریانسازی (STREAMING)
با استفاده از گزینه --stream، دستور jq میتواند متنهای ورودی را به صورت جریانی (streaming) تجزیه کند، که به برنامههای jq اجازه میدهد پردازش متنهای بزرگ JSON را بلافاصله آغاز کنند، به جای آنکه منتظر بمانند تا تجزیه کامل شود. اگر یک متن JSON با حجم ۱ گیگابایت داشته باشید، جریانسازی آن به شما امکان میدهد تا بسیار سریعتر آن را پردازش کنید.
با این حال، کار با جریانسازی آسان نیست زیرا برنامه jq ساختار [<path>, <leaf-value>] (و چند قالب دیگر) را به عنوان ورودی خواهد داشت.
چندین تابع توکار برای آسانتر کردن کار با جریانها ارائه شده است.
مثالهای زیر از شکل جریانی ["a",["b"]] استفاده میکنند که برابر با [[0],"a"],[[1,0],"b"],[[1,0]],[[1]] است.
قالبهای جریانی شامل [<path>, <leaf-value>] (برای نشان دادن هر مقدار اسکالر، آرایه خالی، یا شیء خالی) و [<path>] (برای نشان دادن انتهای یک آرایه یا شیء) هستند. نسخههای آینده jq که با --stream و --seq اجرا میشوند ممکن است هنگام عدم موفقیت در تجزیه متن ورودی، قالبهای اضافی دیگری مانند ["error message"] را خروجی دهند.
truncate_stream(stream_expression)
یک عدد را به عنوان ورودی دریافت میکند و تعداد متناظری از عناصر مسیر را از سمت چپ خروجیهای عبارت جریانی دادهشده برش میدهد (حذف میکند).
-
jq ´truncate_stream([[0],"a"],[[1,0],"b"],[[1,0]],[[1]])´ 1 => [[0],"b"], [[0]]
fromstream(stream_expression)
مقادیر متناظر با خروجیهای عبارت جریان (stream expression) را در خروجی قرار میدهد.
-
jq ´fromstream(1|truncate_stream([[0],"a"],[[1,0],"b"],[[1,0]],[[1]]))´ null => ["b"]
tostream
تابع توکار tostream شکل جریانی ورودی خود را خروجی میدهد.
-
jq ´. as $dot|fromstream($dot|tostream)|.==$dot´ [0,[1,{"a":1},{"b":2}]] => true
انتساب (ASSIGNMENT)
عمل انتساب در jq اندکی متفاوت از بیشتر زبانهای برنامهنویسی کار میکند. دستور jq تفاوتی میان ارجاعها و کپیها از یک موجودیت قائل نمیشود - دو شیء یا آرایه یا با هم برابرند یا برابر نیستند، بدون هیچ مفهوم دیگری مبنی بر "شیء یکسان" بودن یا "شیء یکسان نبودن".
اگر یک شیء دارای دو فیلد آرایهای .foo و .bar باشد، و شما مقداری را به .foo ضمیمه کنید، اندازه .bar بزرگتر نخواهد شد، حتی اگر پیشتر مقدار .bar = .foo را تنظیم کرده باشید. اگر به برنامهنویسی در زبانهایی مانند Python، Java، Ruby، JavaScript و غیره عادت دارید، میتوانید اینگونه تصور کنید که jq پیش از انجام انتساب، یک کپی عمیق کامل از هر شیء ایجاد میکند (برای کارایی بالا در عمل چنین کاری نمیکند، اما ایده کلی همین است).
این بدان معناست که ساخت مقادیر حلقوی در jq غیرممکن است (مانند آرایهای که نخستین عنصر آن خودش باشد). این رفتار کاملاً عمدی است و تضمین میکند هر چیزی که یک برنامه jq تولید میکند، قابل نمایش در JSON باشد.
تمامی عملگرهای انتساب در jq دارای عبارات مسیر در سمت چپ (LHS) هستند. سمت راست (RHS) مقادیری را فراهم میکند که باید در مسیرهای مشخصشده توسط عبارات مسیر سمت چپ تنظیم شوند.
مقادیر در jq همواره تغییرناپذیر هستند. در لایههای درونی، انتساب با استفاده از یک کاهش برای محاسبه مقادیر جدید و جایگزین برای . کار میکند که تمامی انتسابهای مورد نظر را روی . اعمال کرده و سپس مقدار تغییریافته را در خروجی قرار میدهد. این موضوع با این مثال روشنتر میشود: {a:{b:{c:1}}} | (.a.b|=3), .. این دستور مقادیر {"a":{"b":3}} و {"a":{"b":{"c":1}}} را در خروجی تولید میکند زیرا آخرین زیرعبارت، یعنی .، مقدار اصلی را میبیند، نه مقدار تغییریافته را.
بیشتر کاربران ترجیح میدهند به جای = از عملگرهای انتساب اصلاحی مانند |= یا += استفاده کنند.
توجه داشته باشید که سمت چپ (LHS) عملگرهای انتساب به مقداری در . اشاره دارد. بنابراین عبارت $var.foo = 1 آنطور که انتظار میرود کار نخواهد کرد ($var.foo یک عبارت مسیر معتبر یا کاربردی در . نیست)؛ در عوض از $var | .foo = 1 استفاده کنید.
همچنین توجه داشته باشید که عبارت .a,.b=0 مقادیر .a و .b را تنظیم نمیکند، اما (.a,.b)=0 هر دو را تنظیم میکند.
Update-assignment: |=
این عملگر «بهروزرسانی» |= است. این عملگر یک فیلتر را در سمت راست دریافت کرده و با اجرای مقدار پیشین از طریق این عبارت، مقدار جدید را برای ویژگیِ در حال انتساب در . محاسبه میکند. برای نمونه، عبارت (.foo, .bar) |= .+1 شیئی میسازد که فیلد foo آن برابر با foo ورودی به علاوه ۱، و فیلد bar آن برابر با bar ورودی به علاوه ۱ تنظیم شده است.
سمت چپ میتواند هر عبارت مسیر عمومی باشد؛ path() را ببینید.
توجه داشته باشید که سمت چپ |= به مقداری در . اشاره دارد. بنابراین عبارت $var.foo |= . + 1 آنطور که انتظار میرود کار نخواهد کرد ($var.foo یک عبارت مسیر معتبر یا کاربردی در . نیست)؛ در عوض از $var | .foo |= . + 1 استفاده کنید.
اگر سمت راست هیچ مقداری تولید نکند (یعنی empty باشد)، مسیر سمت چپ مانند عملکرد del(path) حذف خواهد شد.
اگر سمت راست چندین مقدار در خروجی تولید کند، تنها مقدار اول استفاده خواهد شد (نکته سازگاری: در jq نسخه 1.5 و نسخههای پیشین، تنها آخرین مقدار استفاده میشد).
-
jq ´(..|select(type=="boolean")) |= if . then 1 else 0 end´ [true,false,[5,true,[true,[false]],false]] => [1,0,[5,1,[1,[0]],0]]
Arithmetic update-assignment: +=, -=, *=, /=, %=, //=
دستور jq چند عملگر به فرم a op= b دارد که همگی معادل a |= . op b هستند. بنابراین، += 1 میتواند برای افزایش مقادیر به کار رود، که همانند |= . + 1 است.
-
jq ´.foo += 1´ {"foo": 42} => {"foo": 43}
Plain assignment: =
این عملگر انتساب ساده است. بر خلاف سایر عملگرها، ورودی سمت راست (RHS) همان ورودی سمت چپ (LHS) است نه مقدار موجود در مسیر سمت چپ، و تمامی مقادیر خروجی سمت راست استفاده خواهند شد (همانطور که در زیر نشان داده شده است).
اگر سمت راست = چندین مقدار تولید کند، آنگاه jq به ازای هر مقدار، مسیرهای سمت چپ را روی آن مقدار تنظیم کرده و سپس . تغییریافته را در خروجی قرار میدهد. برای نمونه، (.a,.b) = range(2) ابتدا {"a":0,"b":0} و سپس {"a":1,"b":1} را در خروجی تولید میکند. حالتهای انتساب «بهروزرسانی» (بالا را ببینید) چنین کاری نمیکنند.
این مثال تفاوت میان = و |= را نشان میدهد:
ورودی {"a": {"b": 10}, "b": 20} را به برنامههای زیر بدهید:
-
.a = .b
و
-
.a |= .b
اولی فیلد a ورودی را برابر با فیلد b ورودی قرار میدهد و خروجی {"a": 20, "b": 20} را تولید میکند. دومی فیلد a ورودی را برابر با فیلد b از فیلد a قرار میدهد و خروجی {"a": 10, "b": 20} را تولید میکند.
-
jq ´.a = .b´ {"a": {"b": 10}, "b": 20} => {"a":20,"b":20} jq ´.a |= .b´ {"a": {"b": 10}, "b": 20} => {"a":10,"b":20} jq ´(.a, .b) = range(3)´ null => {"a":0,"b":0}, {"a":1,"b":1}, {"a":2,"b":2} jq ´(.a, .b) |= range(3)´ null => {"a":0,"b":0}
Complex assignments
در سمت چپ یک انتساب در jq، موارد بسیار بیشتری نسبت به بیشتر زبانها مجاز است. پیش از این دسترسیهای ساده به فیلدها را در سمت چپ دیدهایم، و جای تعجب نیست که دسترسی به عناصر آرایه نیز به همان خوبی کار میکند:
-
.posts[0].title = "JQ Manual"
آنچه ممکن است شگفتآور باشد این است که عبارت سمت چپ میتواند چندین نتیجه تولید کند که به بخشهای متفاوتی از سند ورودی اشاره دارند:
-
.posts[].comments |= . + ["this is great"]
آن مثال رشته "this is great" را به آرایه "comments" از هر پست در ورودی ضمیمه میکند (جایی که ورودی شیئی با فیلد "posts" است که خود آرایهای از پستهاست).
هنگامی که jq با انتسابی مانند ´a = b´ مواجه میشود، هنگام اجرای a، «مسیر» طیشده برای انتخاب بخشی از سند ورودی را ثبت میکند. این مسیر سپس برای یافتن بخشی از ورودی که باید هنگام اجرای انتساب تغییر کند، استفاده میشود. هر فیلتری میتواند در سمت چپ علامت مساوی قرار گیرد - مسیرهایی که از ورودی انتخاب میکند، همان جاهایی خواهند بود که انتساب روی آنها انجام میشود.
این یک عملیات بسیار قدرتمند است. فرض کنید بخواهیم با استفاده از همان ورودی "blog" بالا، نظری به پستهای وبلاگ اضافه کنیم. این بار، تنها میخواهیم روی پستهایی نظر بگذاریم که توسط "stedolan" نوشته شدهاند. میتوانیم آن پستها را با استفاده از تابع "select" که پیشتر شرح داده شد پیدا کنیم:
-
.posts[] | select(.author == "stedolan")
مسیرهای فراهمشده توسط این عملیات به هر یک از پستهایی که "stedolan" نوشته است اشاره میکنند، و میتوانیم روی هر یک از آنها به همان روش قبلی نظر بگذاریم:
-
(.posts[] | select(.author == "stedolan") | .comments) |= . + ["terrible."]
توضیحات / کامنتها (COMMENTS)
میتوانید در فیلترهای jq خود با استفاده از # کامنت (توضیحات) بنویسید.
یک نویسه # (که بخشی از یک رشته نباشد) آغازگر کامنت است. تمامی نویسهها از # تا پایان خط نادیده گرفته میشوند.
اگر پیش از پایان خط، تعداد فردی نویسه بکاسلش وجود داشته باشد، خط بعدی نیز بخشی از کامنت به شمار آمده و نادیده گرفته میشود.
برای نمونه، کد زیر مقدار [1,3,4,7] را در خروجی تولید میکند:
-
[ 1, # foo \ 2, # bar \\ 3, 4, # baz \\\ 5, \ 6, 7 # comment \ comment \ comment ]
ادامه دادن کامنت در خط بعد با بکاسلش میتواند هنگام نوشتن «شِبَنگ» (shebang) برای یک اسکریپت jq کاربردی باشد:
-
#!/bin/sh -- # total - Output the sum of the given arguments (or stdin) # usage: total [numbers...] # \ exec jq --args -MRnf -- "$0" "$@" $ARGS.positional | reduce ( if . == [] then inputs else .[] end | . as $dot | try tonumber catch false | if not or isnan then @json "total: Invalid number \($dot).\n" | halt_error(1) end ) as $n (0; . + $n)
خط exec توسط jq یک کامنت به شمار آمده و نادیده گرفته میشود. اما توسط sh نادیده گرفته نمیشود، چرا که در sh بکاسلش در انتهای خط باعث ادامه یافتن کامنت نمیشود. با این ترفند، هنگامی که اسکریپت به صورت total 1 2 فراخوانی شود، دستور /bin/sh -- /path/to/total 1 2 اجرا خواهد شد و سپس sh دستور exec jq --args -MRnf -- /path/to/total 1 2 را اجرا میکند و خود را با یک مفسر jq فراخوانیشده با گزینههای مشخصشده (-M، -R، -n، --args) جایگزین مینماید که فایل جاری ($0) را همراه با آرگومانهایی ($@) که به sh ارسال شده بودند ارزیابی میکند.
ماژولها (MODULES)
دستور jq دارای سیستم کتابخانه/ماژول است. ماژولها فایلهایی هستند که نام آنها به .jq ختم میشود.
ماژولهای واردشده (imported) توسط یک برنامه در یک مسیر جستجوی پیشفرض جستجو میشوند (پایین را ببینید). دستورالعملهای import و include به واردکننده اجازه میدهند این مسیر را تغییر دهد.
مسیرهای موجود در مسیر جستجو مشمول جایگزینیهای گوناگونی میشوند.
برای مسیرهایی که با ~/ آغاز میشوند، دایرکتوری خانگی کاربر به جای ~ جایگزین میشود.
برای مسیرهایی که با $ORIGIN/ آغاز میشوند، دایرکتوری محل قرارگیری فایل اجرایی jq به جای $ORIGIN جایگزین میشود.
برای مسیرهایی که با ./ آغاز میشوند یا مسیرهایی که صرفاً . هستند، مسیر فایل شاملکننده (including file) به جای . جایگزین میشود. برای برنامههای سطح بالا که در خط فرمان داده میشوند، دایرکتوری جاری استفاده میشود.
دستورالعملهای import میتوانند به صورت اختیاری یک مسیر جستجو مشخص کنند که مسیر پیشفرض به انتهای آن ضمیمه میشود.
مسیر جستجوی پیشفرض، مسیر جستجوی ارائهشده به گزینه خط فرمان -L است، و در غیر این صورت برابر با ["~/.jq", "$ORIGIN/../lib/jq", "$ORIGIN/../lib"] خواهد بود.
عناصر مسیر Null و رشتههای خالی، پردازش مسیر جستجو را خاتمه میدهند.
یک وابستگی با مسیر نسبی foo/bar در مسیرهای foo/bar.jq و foo/bar/bar.jq در مسیر جستجوی دادهشده جستجو میشود. این قابلیت به این منظور طراحی شده که ماژولها بتوانند همراه با فایلهای کنترل نسخه، فایلهای README و غیره درون یک پوشه قرار گیرند، و همچنین امکان ایجاد ماژولهای تکفایلی را فراهم سازد.
بخشهای متوالی با نام یکسان برای جلوگیری از ابهام مجاز نیستند (برای نمونه foo/foo).
برای مثال، با -L$HOME/.jq ماژول foo را میتوان در $HOME/.jq/foo.jq و $HOME/.jq/foo/foo.jq یافت.
اگر فایل .jq در دایرکتوری خانگی کاربر وجود داشته باشد و یک فایل باشد (نه یک پوشه)، به صورت خودکار در برنامه اصلی بارگذاری (source) میشود.
import RelativePathString as NAME [<metadata>];
یک ماژول یافتشده در مسیر دادهشده به صورت نسبی نسبت به یک دایرکتوری در مسیر جستجو را وارد میکند. پسوند .jq به رشته مسیر نسبی اضافه خواهد شد. نمادهای ماژول با پیشوند NAME:: مشخص میشوند.
متادیتای اختیاری باید یک عبارت ثابت jq باشد. این باید شیئی با کلیدهایی مانند homepage و غیره باشد. در حال حاضر jq تنها از کلید/مقدار search در متادیتا استفاده میکند. همچنین متادیتا از طریق تابع توکار modulemeta در دسترس کاربران قرار میگیرد.
کلید search در متادیتا، در صورت وجود، باید دارای مقداری از نوع رشته یا آرایه (آرایهای از رشتهها) باشد؛ این مسیر جستجو به ابتدای مسیر جستجوی سطح بالا افزوده خواهد شد.
include RelativePathString [<metadata>];
یک ماژول یافتشده در مسیر دادهشده به صورت نسبی نسبت به یک دایرکتوری در مسیر جستجو را به گونهای وارد میکند که گویی در همان محل گنجانده شده است. پسوند .jq به رشته مسیر نسبی اضافه خواهد شد. نمادهای ماژول به گونهای به فضای نام (namespace) فراخواننده وارد میشوند که گویی محتوای ماژول مستقیماً گنجانده شده است.
متادیتای اختیاری باید یک عبارت ثابت jq باشد. این باید شیئی با کلیدهایی مانند homepage و غیره باشد. در حال حاضر jq تنها از کلید/مقدار search در متادیتا استفاده میکند. همچنین متادیتا از طریق تابع توکار modulemeta در دسترس کاربران قرار میگیرد.
import RelativePathString as $NAME [<metadata>];
یک فایل JSON یافتشده در مسیر دادهشده به صورت نسبی نسبت به یک دایرکتوری در مسیر جستجو را وارد میکند. پسوند .json به رشته مسیر نسبی اضافه خواهد شد. دادههای فایل به صورت $NAME::NAME در دسترس خواهند بود.
متادیتای اختیاری باید یک عبارت ثابت jq باشد. این باید شیئی با کلیدهایی مانند homepage و غیره باشد. در حال حاضر jq تنها از کلید/مقدار search در متادیتا استفاده میکند. همچنین متادیتا از طریق تابع توکار modulemeta در دسترس کاربران قرار میگیرد.
کلید search در متادیتا، در صورت وجود، باید دارای مقداری از نوع رشته یا آرایه (آرایهای از رشتهها) باشد؛ این مسیر جستجو به ابتدای مسیر جستجوی سطح بالا افزوده خواهد شد.
module <metadata>;
این دستورالعمل کاملاً اختیاری است. برای عملکرد صحیح نیازی به آن نیست. تنها با هدف ارائه متادیتایی است که میتواند با تابع توکار modulemeta خوانده شود.
متادیتا باید یک عبارت ثابت jq باشد. این باید شیئی با کلیدهایی مانند homepage باشد. در حال حاضر jq از این متادیتا استفاده نمیکند، اما از طریق تابع توکار modulemeta در دسترس کاربران قرار میگیرد.
modulemeta
یک نام ماژول را به عنوان ورودی دریافت کرده و متادیتای ماژول را به عنوان یک شیء در خروجی تولید میکند، که در آن وارداتهای ماژول (شامل متادیتا) به عنوان یک آرایه برای کلید deps و توابع تعریفشده ماژول به عنوان یک آرایه برای کلید defs قرار دارند.
برنامهها میتوانند از این تابع برای پرسوجوی متادیتای یک ماژول استفاده کنند، که سپس میتوانند از آن برای نمونه جهت جستجو، دانلود و نصب وابستگیهای ناموجود بهره ببرند.
رنگها (COLORS)
برای پیکربندی رنگهای جایگزین، کافی است متغیر محیطی JQ_COLORS را روی فهرستی از دنبالههای فرار جزئی ترمینال که با دونقطه از هم جدا شدهاند (مانند "1;31") به این ترتیب تنظیم کنید:
- رنگ برای null
- رنگ برای false
- رنگ برای true
- رنگ برای اعداد
- رنگ برای رشتهها
- رنگ برای آرایهها
- رنگ برای اشیاء
- رنگ برای کلیدهای شیء
طرح رنگی پیشفرض معادل مقداردهی JQ_COLORS="0;90:0;39:0;39:0;39:0;32:1;39:1;39:1;34" است.
این یک کتابچه راهنما برای کدهای فرار VT100/ANSI نیست. با این حال، هر یک از این مشخصات رنگی باید از دو عدد تشکیل شود که با یک نقطه-ویرگول از هم جدا شدهاند، که در آن عدد اول یکی از موارد زیر است:
- 1 (روشن)
- 2 (کمرنگ)
- 4 (زیرخط)
- 5 (چشمکزن)
- 7 (معکوس)
- 8 (مخفی)
و عدد دوم یکی از این موارد است:
- 30 (سیاه)
- 31 (قرمز)
- 32 (سبز)
- 33 (زرد)
- 34 (آبی)
- 35 (ارغوانی)
- 36 (فیروزهای)
- 37 (سفید)
اشکالات (BUGS)
احتمالاً وجود دارند. آنها را گزارش کرده یا دربارهشان گفتگو کنید در:
نویسنده (AUTHOR)
Stephen Dolan <mu@netsoc.tcd.ie>
| May 2025 |