| JAQ(1) | General Commands Manual | JAQ(1) |
نام (NAME)
jaq - پردازشگر خط فرمان JSON
خلاصه دستور (SYNOPSIS)
jaq [OPTION]… [FILTER] [FILE]…
توضیحات (DESCRIPTION)
دستور jaq مفسری برای زبان برنامهنویسی jq است. این ابزار به گونهای طراحی شده است که بتواند به عنوان جایگزین مستقیم برای برنامه jq (که مفسر مرجع زبان jq نوشتهشده به زبان C است) استفاده شود.
ابزار jaq که به زبان Rust نوشته شده است، بر درستی، کارایی بالا و سادگی تمرکز دارد. علاوه بر این، jaq قابلیتهایی را اضافه میکند که در jq وجود ندارند:
- •
- پشتیبانی از چندین قالب فایلی، از جمله JSON، YAML، CBOR، TOML، XML
- •
- پشتیبانی از واحدهای کُد نامعتبر UTF-8 در رشتههای متنی
- •
- رشتههای بایتی (Byte strings)
- •
- اشیاء با کلیدهای غیررشتهای، مانند {0: 1, [2]: 3}
- •
- جایگزینی درجا (In-place) در فایلهای ورودی
این راهنما تلاش دارد:
- •
- قابلفهم، موجز و کامل باشد.
- •
- تمام عملکردهای jaq، به ویژه رابط خط فرمان jaq، زبان هسته jq و کتابخانه استاندارد jq را مستند کند. این راهنما مفاهیمی مشابه راهنمای jq را پوشش میدهد.
- •
- تمام تفاوتها و انحرافها میان jq و jaq را ثبت کند.
در صورتی که این راهنما نقصی در دستیابی به این اهداف دارد، لطفاً یک گزارش اشکال (issue) باز کنید یا یک پولریکوئست (درخواست ادغام) ارسال نمایید. همچنین اگر مایل به پیشنهاد قابلیتهای جدید برای jaq هستید، همین کار را انجام دهید. این پروژه با مشارکتهای شما زنده است!
ایجاد این صفحه راهنما از طریق NGI0 Commons Fund تامین مالی شده است؛ صندوقی که توسط NLnet با حمایت مالی برنامه Next Generation Internet کمیسیون اروپا، تحت نظارت DG Communications Networks, Content and Technology تحت قرارداد کمک مالی شماره 101135429 تاسیس شده است. تامین مالی تکمیلی توسط کتابت دولتی سوئیس برای آموزش، پژوهش و نوآوری (SERI) فراهم شده است.
سازگاری
رابط خط فرمان (COMMAND-LINE INTERFACE)
اجرای jaq [OPTION]… [FILTER] [FILE]… مراحل زیر را انجام میدهد:
- •
- تجزیه (parse) کردن FILTER به عنوان برنامه jq؛ بخش زبان jq را ببینید
- •
- به ازای هر FILE:
- •
- تجزیه کردن FILE به جریانی از مقادیر
- •
- به ازای هر مقدار ورودی در فایل:
- •
- اجرای FILTER روی مقدار ورودی و چاپ مقادیر خروجی آن
- •
- اگر در هر نقطهای خطای مهارنشدهای رخ دهد، jaq متوقف میشود.
برای نمونه، دستور jaq '.name?' persons.json ابتدا فیلتر .name? را تجزیه میکند، سپس تمام مقادیر موجود در persons.json را یکییکی میخواند. سپس فیلتر .name? را روی هر یک از مقادیر اجرا کرده و خروجیهای فیلتر را به صورت JSON چاپ میکند.
ورودی (INPUT)
اگر هیچ FILE مشخص نشود، jaq از ورودی استاندارد میخواند. دستور jaq قالب تجزیه FILE را به شرح زیر تعیین میکند:
- •
- اگر گزینه --from FORMAT استفاده شود، jaq از همان قالب استفاده میکند.
- •
- در غیر این صورت، اگر FILE پسوند فایلی شناختهشده توسط jaq داشته باشد، مانند .json، .yaml، .cbor، .toml، .xml، .csv، .tsv، دستور jaq از قالب متناظر آن استفاده میکند.
- •
- در غیر این صورت، jaq فرض میکند قالب ورودی JSON است.
--from FORMAT
تفسیر تمام فایلهای ورودی در قالب FORMAT. برای نمونه: jaq --from yaml . myfile.yml فایل myfile.yml را به عنوان YAML تجزیه میکند. مقادیر ممکن برای FORMAT عبارتند از: raw، raw0، json، yaml، cbor، toml، xml، csv، tsv.
دستور jaq به صورت خودکار قالب ورودی متناظر را برای فایلهای با پسوندهای زیر انتخاب میکند: .json، .yaml، .yml، .cbor، .toml، .xml، .xhtml، .csv، .tsv. این بدان معناست که دستور jaq --from cbor . myfile.cbor معادل است با jaq . myfile.cbor.
سازگاری
-n, --null-input
ارسال مقدار null به عنوان ورودی به برنامه اصلی، بدون توجه به فایلهای ورودی. برای نمونه:
-
$ yes | jaq -n null
این نشان میدهد که این گزینه در واقع هیچ ورودیای نمیخواند.
ورودیها همچنان از طریق فیلتر inputs قابل دستیابی هستند. برای نمونه:
-
$ yes true | jaq -n 'first(inputs)' true
این میتواند برای پیمایش و تجمیع روی تمام ورودیها با reduce / foreach مفید باشد.
-R, --raw-input
خواندن خطوط ورودی به عنوان توالیای از رشتهها. برای نمونه:
-
$ printf "Hello\nWorld" | jaq -R "Hello" "World"
هنگامی که با گزینه --slurp ترکیب شود، کل ورودی را به عنوان یک رشته منفرد تولید میکند. برای نمونه:
-
$ printf "Hello\nWorld" | jaq -Rs "Hello\nWorld"
گزینه --rawfile را ببینید.
این گزینه معادل --from raw است.
--raw-input0
خواندن مقادیر مشابه --raw-input، با جداسازی توسط NUL (\0) به جای خطوط جدید. برای نمونه:
-
$ printf "Hello\nWorld\0foo" | jaq --raw-input0 "Hello\nWorld" "foo"
هنگامی که با گزینه --slurp ترکیب شود، تمام ورودیها را در قالب یک آرایه جمعآوری میکند. برای نمونه:
-
$ printf "Hello\nWorld\0foo" | jaq -sc --raw-input0 ["Hello\nWorld","foo"]
این گزینه میتواند برای بازگرداندن خروجی حاصل از --raw-output0 استفاده شود:
-
$ printf '"some string\\nwith a newline" "another string"' | jaq --raw-output0 | jaq --raw-input0 "some string\nwith a newline" "another string"
این گزینه معادل --from raw0 است.
سازگاری
-s, --slurp
خواندن تمام مقادیر ورودی درون یک آرایه (به ازای هر فایل). برای نمونه:
-
$ echo 1 2 3 | jaq 1 2 3 $ echo 1 2 3 | jaq -cs [1,2,3]
هنگامی که با گزینه --raw-input ترکیب شود، دستور jaq کل ورودی را به عنوان یک رشته واحد میخواند. برای نمونه:
-
$ echo 1 2 3 | jaq -Rs "1 2 3\n"
گزینه --rawfile را ببینید.
سازگاری
خروجی (OUTPUT)
--to FORMAT
چاپ تمام مقادیر خروجی در قالب FORMAT دادهشده. هر قالبی که توسط --from پذیرفته میشود میتواند در اینجا نیز به کار رود.
توجه داشته باشید که هر مقداری را نمیتوان در هر قالبی چاپ کرد. برای نمونه، در TOML الزامی است که مقدار ریشه یک شیء (object) باشد، بنابراین تلاش برای چاپ یک مقدار ریشه غیرشیئی به عنوان TOML خطایی ایجاد میکند:
-
$ echo [] | jaq --to toml 2>/dev/null; echo $? 2
سازگاری
-c, --compact-output
چاپ خروجی JSON به صورت فشرده، با حذف فاصلههای خالی اضافه. برای نمونه:
-
$ echo [1, 2, 3] | jaq -c [1,2,3]
-r, --raw-output
نوشتن رشتهها (متنی و بایتی) بدون اسکیپ کردن آنها و بدون قرار دادن درون گیومه (کوتیشن). برای نمونه:
-
$ printf '"Hello\\nWorld"' | jaq -r Hello World $ printf '"Hello\\nWorld"' | jaq "Hello\nWorld"
این گزینه روی رشتههای موجود درون مقادیر دیگر، یعنی درون آرایهها و اشیاء تأثیری ندارد. برای نمونه:
-
$ printf '["Hello\\nWorld"]' | jaq -rc ["Hello\nWorld"]
این گزینه معادل --to raw است.
--raw-output0
خروجی دادن مقادیر مانند --raw-output، با خاتمه دادن به آنها با کاراکتر NUL (\0) به جای LF (\n).
-
$ printf '"some string\\nwith a newline" true' | jaq --raw-output0 | jaq -s --raw-input tobytes b"some string\nwith a newline\x00true\x00"
هر مقدار خروجی که یک رشته حاوی NUL باشد، خطایی تولید میکند. توجه داشته باشید که این موضوع در مورد رشتههای موجود در یک آرایه یا شیء صدق نمیکند.
-
$ printf '"string with NUL \\u0000"' | jaq --raw-output0 2>/dev/null; echo $? 2
این گزینه معادل --to raw0 است.
-j, --join-output
عدم چاپ نویسه خط جدید پس از هر مقدار. برای نمونه:
-
$ echo true false | jaq -j && echo " (no new line at end)" truefalse (no new line at end)
این گزینه به ویژه در ترکیب با --raw-output (-r) مفید است؛ برای نمونه:
-
$ printf '"Hello" " " "World" "\\n"' | jaq -jr Hello World
-i, --in-place
بازنویسی فایل ورودی با خروجی آن به صورت درجا (in-place). برای نمونه:
-
$ echo 1 > tmp.json && jaq -i '.+1' tmp.json && cat tmp.json && rm tmp.json 2
این دستور فایل tmp.json را خوانده و آن را با خروجی فیلتر .+1 بازنویسی میکند. توجه داشته باشید که فایل ورودی تنها زمانی بازنویسی میشود که هیچ خروجی دیگری باقی نمانده باشد و هیچ خطایی نیز رخ نداده باشد.
سازگاری
-S, --sort-keys
چاپ اشیاء به صورت مرتبشده بر اساس کلیدهایشان. برای نمونه:
-
$ echo '{"b": {"d": 3, "c": 2}, "a": 1}' | jaq -cS {"a":1,"b":{"c":2,"d":3}} $ echo '{"b": {"d": 3, "c": 2}, "a": 1}' | jaq -c {"b":{"d":3,"c":2},"a":1}
-C, --color-output
رنگی کردن همیشگی خروجی، حتی اگر jaq در یک ترمینال چاپ نشود. برای نمونه:
-
$ echo {} | jaq -C | jaq --from raw tobytes b"\x1b[1;39m{\x1b[0m\x1b[1;39m}\x1b[0m" $ echo {} | jaq | jaq --from raw tobytes b"{}"
(در اینجا، jaq --from raw tobytes یک نمایش بایتی از ورودی خود را چاپ میکند که در حالت -C شامل توالیهای رنگی ANSI است.)
-M, --monochrome-output
عدم استفاده از رنگ در خروجی (خروجی تکرنگ).
--tab
استفاده از تب (tab) برای تورفتگی به جای فاصلهها (spaces).
برای نمونه:
-
$ echo [1, [2]] | jaq --tab | jaq -Rs "[\n\t1,\n\t[\n\t\t2\n\t]\n]\n" $ echo [1, [2]] | jaq | jaq -Rs "[\n 1,\n [\n 2\n ]\n]\n"
--indent N
استفاده از N فاصله برای تورفتگی (پیشفرض: 2).
NO_COLOR
اگر متغیر محیطی NO_COLOR روی یک مقدار غیرخالی تنظیم شده باشد، دستور jaq هیچ رنگی چاپ نمیکند، مگر اینکه فلگ -C استفاده شده باشد.
JQ_COLORS
متغیر محیطی JQ_COLORS فهرستی جداشده با دونقطه (colon) است که در آن هر مدخل سبکی را برای چاپ نوع خاصی از مقادیر مشخص میکند. مدخلهای JQ_COLORS به ترتیب زیر به انواع مقادیر متناظر میشوند:
- •
- null
- •
- false
- •
- true
- •
- اعداد (numbers)
- •
- رشتهها (strings)
- •
- آرایهها (arrays)
- •
- اشیاء (objects)
- •
- کلیدهای اشیاء (object keys)
اگر JQ_COLORS تنظیم نشده باشد، مقدار پیشفرض آن 90:39:39:39:32:1;39:1;39:1;34 در نظر گرفته میشود. این بدان معناست که به طور پیشفرض:
- •
- مقدار null دارای سبک 90 است (رنگ پیشزمینه خاکستری).
- •
- مقادیر false/true/اعداد دارای سبک 39 هستند (رنگ پیشزمینه پیشفرض).
- •
- رشتهها دارای سبک 32 هستند (رنگ پیشزمینه سبز).
- •
- آرایهها/اشیاء دارای سبک 1;39 هستند (پررنگ و رنگ پیشزمینه پیشفرض).
- •
- کلیدهای اشیاء دارای سبک 1;34 هستند (پررنگ و رنگ پیشزمینه آبی).
هر مدخل غیرخالی در JQ_COLORS، مانند 90 یا 1;39، به یک توالی کنترلی «Select Graphic Rendition» (SGR) تبدیل میشود. برای نمونه:
- •
- 32 به \x1b[32m تبدیل میشود.
- •
- 1;39 به \x1b[1;39m تبدیل میشود.
دستور jaq اجازه تغییر رنگ رشتههای بایتی را نمیدهد، زیرا jq هیچ امکانی برای این کار فراهم نکرده است.
کامپایل (COMPILATION)
اگر هیچ FILTER ارائه نشود، jaq از . (فیلتر همانی) به عنوان فیلتر استفاده میکند.
هنگام ارسال مستقیم فیلترها به عنوان آرگومان FILTER در خط فرمان، باید مراقب بود که فیلتر به درستی اسکیپ (escape) شود. نحوه انجام این کار به پلتفرم بستگی دارد، اما در سیستمهای شبهیونیکس، قرار دادن فیلتر درون کوتیشنهای تکی (') و جایگزینی رخدادهای ' در فیلترها با '\'' کفایت میکند. برای نمونه، جهت اجرای فیلتر "'" که رشتهای شامل یک کوتیشن تکی تولید میکند، میتوانید بنویسید:
-
$ jaq -n '"'\''"' "'"
اجرای فیلترهایی که با عملگر نفی آغاز میشوند، مانند jaq '-1'، با شکست مواجه میشود زیرا - به عنوان شروع یک سوییچ خط فرمان تفسیر میشود نه نفی. میتوانید با استفاده از -- یا قرار دادن فیلتر در پرانتز این مشکل را برطرف کنید:
-
$ jaq -n -- '-1' -1 $ jaq -n '(-1)' -1
-f, --from-file
خواندن فیلتر از فایلی که نام آن در آرگومان فیلتر مشخص شده است.
با این گزینه، jaq آرگومان FILTER را به عنوان نام فایلی تفسیر میکند که حاوی فیلتر است. توجه داشته باشید که نام فایل ممکن است مستقیماً پس از این گزینه نیاید. برای نمونه:
-
$ echo '2 * 21' > tmp.jq && jaq --from-file -n tmp.jq && rm tmp.jq 42
-L, --library-path DIR
جستجو برای ماژولها و دادهها در دایرکتوری مشخصشده.
دستور jaq ماژولها و دادهها را در مجموعهای از پوشهها به نام «مسیرهای جستجو» جستجو میکند. استفاده از --library-path دایرکتوری جدیدی را به مسیرهای جستجوی سراسری اضافه میکند.
برای نمونه: jaq -L . -L .. 'include "script"; foo' فایل script.jq را ابتدا در پوشه جاری و سپس در پوشه والد جستجو میکند.
اگر گزینه --library-path داده نشود، مسیرهای جستجوی سراسری زیر استفاده میشوند:
- •
- ~/.jq
- •
- $ORIGIN/../lib/jq
- •
- $ORIGIN/../lib
برای جزئیات بیشتر بخش ماژولها را ببینید.
متغیرها (VARIABLES)
--arg A V
تنظیم متغیر $A با مقدار رشتهای V.
برای نمونه:
-
$ jaq --arg name "John Doe" -n '"Welcome, " + $name' "Welcome, John Doe"
--argjson A V
تنظیم متغیر $A با مقدار JSON با نام V.
برای نمونه:
-
$ jaq --argjson song '{"name": "One of Us", "artist": "ABBA", "year": 1981}' -n \ '"Currently playing: \($song.name) (\($song.year))"' "Currently playing: One of Us (1981)"
اگر V بیش از یک مقدار منفرد داشته باشد، مثلاً 1 2، دستور jaq خطایی صادر میکند.
--slurpfile A F
تنظیم متغیر $A با یک آرایه حاوی مقادیر JSON در فایل F.
برای نمونه:
-
$ echo 1 2 3 > tmp.json && jaq --slurpfile xs tmp.json -cn '$xs' && rm tmp.json [1,2,3]
--rawfile A F
تنظیم متغیر $A با رشتهای حاوی محتویات فایل F.
دستور jaq تلاش میکند فایل را از طریق نگاشت حافظه (memory mapping) بارگذاری کند، که زمان ثابت مصرف میکند و امکان بارگذاری فایلهایی را میدهد که در حافظه جا نمیشوند. در صورت عدم موفقیت، jaq فایل را به صورت عادی و با زمان خطی بارگذاری میکند. این دقیقاً همان چیزی است که هنگام استفاده از -Rs (--raw-input و --slurp) جهت بارگذاری یک فایل (برخلاف ورودی استاندارد) رخ میدهد.
سازگاری
--args
جمعآوری آرگومانهای مکانی باقیمانده در $ARGS.positional.
اگر این گزینه داده شود، تمام آرگومانهای بعدی که در غیر این صورت به عنوان فایل ورودی تفسیر میشدند، در قالب یک آرایه در $ARGS.positional جمعآوری میشوند.
برای نمونه:
-
$ touch tmp.json && jaq '$ARGS.positional' tmp.json --args foo -nc bar -- baz -j qux && rm tmp.json ["foo","bar","baz","-j","qux"]
توجه داشته باشید که در اینجا tmp.json و -nc در آرایه جمعآوری نمیشوند — اولی به این دلیل که قبل از --args آمده است، و دومی به این دلیل که به عنوان فایل ورودی تفسیر نمیشده است. با این حال، -j در آرایه جمعآوری میشود زیرا بعد از -- آمده است، که باعث میشود هر آرگومان بعد از آن به عنوان فایل ورودی تلقی شود.
متفرقه (MISCELLANEA)
-e, --exit-status
استفاده از آخرین مقدار خروجی به عنوان کد وضعیت خروج.
این گزینه امکان استفاده از کدهای خروج 1 و 4 را فراهم میکند که در حالت عادی استفاده نمیشوند.
دستور jaq از کدهای وضعیت خروج زیر استفاده میکند:
- •
- 0: بدون خطا.
- •
- 1: آخرین مقدار خروجی false یا null است.
- •
- 2: خطای I/O یا CLI، مانند فایل پیدا نشد یا گزینه ناشناخته در CLI.
- •
- 3: خطای تجزیه/کامپایل فیلتر.
- •
- 4: فیلتر هیچ خروجیای تولید نکرد.
- •
- 5: هر خطای دیگر، مانند فراخوانی فیلتر error.
فیلترهای halt و halt_error میتوانند برای خروج از jaq با کدهای خروج دلخواه استفاده شوند.
برای نمونه:
-
$ jaq -n empty; echo $? 0 $ jaq -n false >/dev/null; echo $? 0 $ jaq -en false >/dev/null; echo $? 1 $ jaq . does_not_exist.json 2>/dev/null; echo $? 2 $ jaq --foo 2>/dev/null; echo $? 2 $ jaq '+' 2>/dev/null; echo $? 3 $ jaq -en empty; echo $? 4 $ jaq -n error 2>/dev/null; echo $? 5 $ jaq -n 'halt(9)'; echo $? 9
-h, --help
چاپ خلاصهای از گزینههای خط فرمان.
-V, --version
چاپ نسخه jaq.
پشتیبانینشده (UNSUPPORTED)
گزینههای خط فرمان زیر توسط jq پشتیبانی میشوند، اما در jaq پشتیبانی نمیشوند:
- •
- --ascii-output, -a
- •
- --unbuffered
- •
- --stream
- •
- --stream-errors
- •
- --seq
- •
- --jsonargs
زبان هسته (CORE LANGUAGE)
زبان jq یک زبان برنامهنویسی تابعی، جریانی (streaming) و غیرحریص (lazy) است که در ابتدا توسط استیون دولان (Stephen Dolan) طراحی شد. زبان jq تورینگ-کامل است و بنابراین میتواند برای نوشتن هر برنامهای که در سایر زبانهای برنامهنویسی قابل نگارش است به کار رود. برنامههای jq با چندین مفسر قابل اجرا هستند، از جمله jq، gojq، fq و jaq.
به برنامهای که به زبان jq نوشته میشود، یک برنامه jq یا فیلتر گفته میشود. فیلتر تابعی است که یک مقدار ورودی دریافت کرده و جریانی از مقادیر خروجی را تولید میکند. طبق قرارداد، از f و g برای نشان دادن فیلترهای دلخواه استفاده میکنیم.
جریان مقادیر خروجی میتواند نامتناهی باشد؛ برای نمونه، فیلتر jq با دستور repeat("Hi") یک توالی نامتناهی از رشتههای "Hi" تولید میکند.
بخشهای زیر تمامی فیلترها را با نحو توکار در jq مستند میکنند. مثالها به صورت 1 + 2, true or false --> 3 true نوشته شدهاند که معادل است با:
-
$ jaq -n '1 + 2, true or false' 3 true
یک فیلتر اتمیک (atomic) فیلتری است که یک عملگر دوتایی (binary operator) نیست. برای نمونه، فیلترهای -1، map(.+1) و .[0] همگی اتمیک هستند، در حالی که فیلترهای 1 + 2، explode | .[] و .[0] += 1 غیراتمیک هستند. برای تبدیل یک فیلتر غیراتمیک f به یک فیلتر اتمیک معادل، آن را درون پرانتز قرار دهید، یعنی (f).
مقادیر (VALUES)
این بخش تمام مقادیر بالقوهای را که فیلترهای jq میتوانند پردازش کنند و نحوه تولید آنها را فهرست میکند.
سازگاری
null
فیلتر null مقدار null را برمیگرداند.
مقدار null به روشهای گوناگون دیگری نیز قابل دستیابی است، مانند ایندکس کردن کلیدی که در آرایه یا شیء وجود ندارد؛ برای نمونه: [] | .[0] --> null یا {} | .a --> null.
بولیها (Booleans)
فیلترهای true و false مقادیر بولی true و false را برمیگردانند. بولیها همچنین میتوانند توسط عملیاتهای مقایسه تولید شوند؛ برای نمونه: 0 == 0 --> true یا [] == {} --> false.
هر مقدار jq را میتوان به یک مقدار بولی نگاشت کرد؛ بدین صورت که null و false دارای مقدار بولی false هستند و تمام مقادیر دیگر دارای مقدار بولی true هستند. این ویژگی برای فیلترهایی مانند if-then-else و // دارای اهمیت است.
اعداد (Numbers)
اعداد فیلترهایی هستند که مقدار متناظر خود را برمیگردانند؛ برای نمونه: 0 --> 0، 3.14 --> 3.14 و 2.99e6 --> 2.99e6. اعداد منفی را میتوان با اعمال عملگر نفی به یک عدد ساخت؛ برای نمونه: -1 --> -1.
به طور داخلی، jaq میان اعداد صحیح (integers)، اعداد ممیز شناور (floats) و اعداد دهدهی (decimals) تمایز قائل میشود:
- •
- یک عدد بدون نقطه (.) و بدون توان (e/E) بدون از دست رفتن دقت به عنوان عدد صحیح ذخیره میشود. دستور jaq میتواند اعدادی با اندازه دلخواه و بزرگ را ذخیره و محاسبه کند؛ برای نمونه: 340282366920938463463374607431768211456 (2^128).
- •
- هر عدد غیرصحیح در ابتدا به صورت یک عدد دهدهی ذخیره میشود، که یک نمایش متنی از آن عدد است. این بدان معناست که jaq هر عددی را که با عبارت منظم فوق مطابقت داشته باشد، مانند 1.0e500، در صورتی که در یک فایل JSON یا فیلتر jq ظاهر شود، بدون افت دقت حفظ میکند.
- •
- هنگام انجام محاسبه با یک عدد دهدهی، دستور jaq آن را به صورت شفاف به یک عدد ممیز شناور ۶۴ بیتی IEEE-754 تبدیل میکند. برای نمونه، 1.0e500 + 1 --> Infinity، زیرا jaq عدد 1.0e500 را به نزدیکترین عدد ممیز شناور که Infinity است تبدیل مینماید.
قوانین محاسباتی jaq به شرح زیر است:
- •
- مجموع، تفاضل، حاصلضرب و باقیمانده دو عدد صحیح، یک عدد صحیح است؛ برای نمونه: 1 + 2 --> 3.
- •
- هر عملیات دیگر میان دو عدد حاصل ممیز شناور تولید میکند؛ برای نمونه: 10 / 2 --> 5.0 و 1.0 + 2 --> 3.0.
میتوانید یک عدد صحیح را به ممیز شناور تبدیل کنید؛ برای نمونه با جمع کردن با 0.0، ضرب در 1.0 یا تقسیم بر 1. همچنین میتوانید یک عدد ممیز شناور را با توابع round، floor یا ceil به عدد صحیح تبدیل کنید؛ برای نمونه: 1.2 | floor, round, ceil --> 1 1 2.
سازگاری
افزون بر این، jq مقدار NaN را به صورت null چاپ میکند؛ برای نمونه nan | tojson مقدار null میدهد. در مقابل، jaq مقدار NaN را به عنوان NaN چاپ میکند؛ برای نمونه nan | tojson --> "NaN". بخش XJON را برای جزئیات ببینید.
رشتههای متنی (Text strings)
یک رشته متنی آرایهای از بایتها است که میتواند با نحو "..." ساخته شود. در اینجا، ... ممکن است شامل هر کاراکتر UTF-8 در محدوده U+0020 تا U+10FFFF باشد، به استثنای '"' و '\'. برای نمونه: "Hello 東京!" --> "Hello 東京!".
علاوه بر این، ... میتواند شامل توالیهای گریز زیر باشد:
- •
- \b، \f، \n، \r، یا \t
- •
- \" یا \\
- •
- \uHHHH که در آن HHHH یک عدد هگزادسیمال است
- •
- \(f) که در آن f یک فیلتر jq است (درونیابی رشتهای / string interpolation)
بیشتر کاراکترهای زیر U+0020 تنها از طریق توالی گریز یونیکد قابل تولید هستند؛ یعنی: "NUL = \u0000" --> "NUL = \u0000".
یک رشته حاوی یک فیلتر درونیابیشده، مانند "...\(f)..."، معادل است با: "..." + (f | tostring) + "...". برای نمونه:
- •
- "Hello \("Alice", "Bob") 😀!\n" --> "Hello Alice 😀!\n" "Hello Bob 😀!\n"
- •
- 41 | "The successor of \(.) is \(.+1)." --> "The successor of 41 is 42.".
یک رشته را شبهشناسه (identifier-like) مینامیم اگر با عبارت منظم [a-zA-Z_][a-zA-Z_0-9]* مطابقت داشته باشد.
رشتهها میتوانند دارای پیشوند @x باشند که در آن x یک شناسه است؛ مانند @uri. در این صورت، فیلترهای رشتههای درونیابیشده به جای tostring از طریق @x لولهکشی (pipe) میشوند، یعنی: @x "...\(f)..." معادل است با @x "..." + (f | @x) + @x "...". هنگامی که "..." شامل هیچ فیلتر درونیابیشدهای نباشد، @x "..." معادل با "..." خواهد بود. برای نمونه: "-[]?" | @uri "https://gedenkt.at/jaq/?q=\(.)" --> "https://gedenkt.at/jaq/?q=-%5B%5D%3F".
رشتههای بایتی (Byte strings)
یک رشته بایتی، آرایهای از بایتها است که به عنوان متن (UTF-8) تفسیر نمیشود. این نوع رشته را میتوان از یک رشته متنی از طریق فیلتر tobytes تولید کرد؛ برای نمونه: "Hello, world! 🙂" | tobytes --> b"Hello, world! \xf0\x9f\x99\x82". در حال حاضر هیچ نحو بومی مستقیمی در jaq برای ساخت رشته بایتی به صورت مستقیم وجود ندارد.
رشتههای بایتی از چند جنبه با رشتههای متنی متفاوت هستند؛ به ویژه اینکه ایندکسگذاری و برشزنی (slicing) آنها در زمان ثابت صورت میگیرد. این ویژگی رشتههای بایتی را برای نمونه جهت تجزیه قالبهای باینری جذاب میکند.
به دلایل سازگاری، jaq هر دو نوع رشتههای متنی و بایتی را به عنوان رشته در نظر میگیرد. بدین معنا که "Hello" | isstring and (tobytes | isstring) --> true. علاوه بر این، یک رشته متنی و یک رشته بایتی که حاوی بایتهای معادل باشند، برابر تلقی میشوند؛ برای نمونه: "Hello" | . == tobytes --> true.
سازگاری
آرایهها (Arrays)
یک آرایه دنبالهای متناهی از مقادیر است.
یک آرایه خالی را میتوان با فیلتر [] ساخت، که شکل خلاصه برای [empty] --> [] است. یک آرایه از مقادیر را میتوان با نحو [f] ساخت، که در آن f یک فیلتر است. فیلتر [f] ورودی خود را به f پاس داده و آن را اجرا میکند، و آرایهای شامل تمام خروجیهای f را بازمیگرداند. اگر f خطایی صادر کند، [f] به جای آرایه آن خطا را برمیگرداند.
این نحو امکان ساخت آرایههایی مانند [1, 2, 3] --> [1, 2, 3] را فراهم میکند. در اینجا، 1, 2, 3 صرفاً فیلتری است که سه عدد تولید میکند (بخش الحاق را ببینید). هیچ نحو اختصاصی برای آرایه به صورت [x1, ..., xn] وجود ندارد. این بدان معنی است که میتوانید از فیلترهای دلخواه در [f] استفاده کنید؛ برای نمونه: [limit(3; repeat(0))] --> [0, 0, 0]. میتوانید از ورودی دادهشده به [f] در داخل f استفاده کنید؛ برای نمونه، میتوان فیلتر قبلی را به طور معادل چنین نوشت: {count: 3, elem: 0} | [limit(.count; repeat(.elem))] --> [0, 0, 0].
اشیاء (Objects)
یک شیء نگاشتی از کلیدها به مقادیر است.
یک شیء خالی را میتوان با فیلتر {} ساخت. یک شیء با یک جفت کلید و مقدار را میتوان با {(k): v} ایجاد کرد، که در آن k و v هر دو فیلتر هستند. برای نمونه: {("a"): 1} --> { "a": 1 }.
اگر k یک رشته مانند "a" یا یک متغیر مانند $k باشد، میتوانید پرانتزها را حذف کنید؛ مانند {"a": 1} یا {$k: 1}. اگر رشته شبهشناسه باشد، میتوانید کوتیشنها را نیز حذف کنید؛ مانند {a: 1}.
برای ساخت یک شیء با چندین جفت کلید-مقدار، میتوانید چندین شیء را با هم جمع کنید؛ مانند: {(k1): v1} + ... + {(kn): vn}. میتوانید این را به صورت فشردهتر چنین بنویسید: {(k1): v1, ..., (kn): vn}. برای نمونه: {a: 1, b: 2} --> { "a": 1, "b": 2 }.
به جای {k: .k} (ایندکسگذاری را ببینید)، میتوانید به طور خلاصه بنویسید {k}؛ برای نمونه: {a: 1, b: 2} | {a} --> { "a": 1 }. همچنین به جای {k: $k}، میتوانید بنویسید {$k}؛ برای نمونه: 1 as $k | {$k} --> { "k": 1 }.
فیلتر {(k): v} معادل با k as $k | v as $v | {$k: $v} است. این بدان معنی است که وقتی k یا v چندین مقدار خروجی تولید میکنند، یک شیء به ازای هر ترکیب خروجی تولید میشود؛ برای نمونه: {("a", "b"): (1, 2)} --> { "a": 1 } { "a": 2 } { "b": 1 } { "b": 2 }. توجه داشته باشید که در اینجا لازم است 1, 2 درون پرانتز قرار گیرد تا مشخص شود که کاما , یک جفت کلید-مقدار جدید را آغاز نمیکند.
سازگاری
-
{(0): 1, "2": 3, ([4]): 5, ({}): 6} --> { 0 : 1, "2": 3, [4] : 5, {} : 6}
توجه داشته باشید که در فیلتر jq، کلیدهای غیررشتهای باید درون پرانتز قرار گیرند، در حالی که در XJON نباید از پرانتز استفاده شود.
این دستور در jq خطا میدهد.
عملگرهای مسیر (PATH OPERATORS)
عملگرهای مسیر در jq بخشهایی از ورودی خود را بازیابی میکنند.
ایندکسگذاری (.[x])
عملگر ایندکسگذاری .[x] عنصر x-ام ورودی را برمیگرداند. برای نمونه:
- •
- [1, 2, 3] | .[1] --> 2
- •
- {a: 1, b: 2} | .["a"] --> 1
اگر هیچ عنصری در ایندکس x در ورودی وجود نداشته باشد، این عملگر null برمیگرداند. برای نمونه:
- •
- [1, 2, 3] | .[3] --> null
- •
- {a: 1, b: 2} | .["c"] --> null
- •
- null | .[0] --> null
این عملگر ورودی رشته بایتی را مانند آرایهای از اعداد در محدوده 0 تا 256 تلقی میکند. برای نمونه: "@ABC" | tobytes | .[0] --> 64، زیرا کاراکتر '@' در UTF-8 به صورت بایت منفرد 64 (0x40) کدگذاری میشود.
برای رشتههای شبهشناسه مانند name، میتوانید شکل خلاصه .name را به جای .["name"] بنویسید؛ مانند: {a: 1} | .a --> 1. این کار همچنین برای رشتههایی که مشابه کلمات کلیدی رزروشده هستند کار میکند؛ مانند: {if: 1} | .if --> 1.
برای ورودی آرایه، میتوانید از اعداد منفی نیز به عنوان ایندکس استفاده کنید، که به عنوان شمارش از انتهای آرایه تفسیر میشوند. برای نمونه: [1, 2, 3] | .[-1] --> 3.
اگر ورودی و ایندکس x هر دو آرایه باشند، همان نتیجه indices(x) بازگردانده میشود. برای نمونه: [1, 2, 0, 1, 2] | .[[1, 2]] --> [0, 3].
اگر ورودی این عملگر یک آرایه یا رشته باشد و ایندکس یک شیء باشد، این عملگر معادل با عملگر برشزنی (slicing) .[x.start : x.end] است. برای نمونه:
- •
- [1, 2, 3] | .[{start: 1 }] --> [2, 3]
- •
- [1, 2, 3] | .[{ end: -1}] --> [1, 2]
- •
- [1, 2, 3] | .[{start: 1, end: -1}] --> [2]
- •
- [1, 2, 3] | .[{ }] --> [1, 2, 3]
این عملگر در صورتی مقداری برمیگرداند که یکی از شرایط زیر برقرار باشد:
- •
- ورودی یک رشته یا آرایه باشد و ایندکس یک شیء باشد (برشزنی)،
- •
- ورودی یک رشته بایتی یا آرایه باشد و ایندکس یک عدد صحیح باشد،
- •
- ورودی و ایندکس هر دو آرایه باشند، یا
- •
- ورودی null یا یک شیء باشد.
در غیر این صورت، خطایی صادر میشود.
سازگاری
برنامه jq هنگامی که ورودی یک رشته یا آرایه باشد و ایندکس شیئی بدون کلیدهای start یا end باشد با شکست مواجه میشود، در حالی که jaq با چنین شیئی مانند {start: null, end: null} رفتار میکند؛ یعنی برشی از ابتدا تا انتها ایجاد میکند.
برشزنی (.[x:y])
عملگر برشزنی .[x:y] برشی از یک رشته یا یک آرایه، از عنصر x-ام تا عنصر y-ام (بدون احتساب خود y) تولید میکند. برای نمونه: [1, 2, 3] | .[1:3] --> [2, 3] و "Hello World!" | .[6:11] --> "World".
هنگام برش دادن یک رشته متنی، عنصر x-ام به x-امین کاراکتر UTF-8 اشاره دارد؛ برای نمونه: "老虎" | .[1:2] --> "虎". از سوی دیگر، هنگام برش دادن یک رشته بایتی، عنصر x-ام به x-امین بایت اشاره دارد؛ برای نمونه: "Färber" | tobytes, . | .[2:] | [., length] --> [b"\xa4rber", 5] ["rber", 4]، زیرا حرف 'ä' دو بایت اما تنها یک کاراکتر اشغال میکند.
همانند عملگر ایندکسگذاری، عملگر برشزنی با ورودی رشته بایتی مانند آرایهای از اعداد رفتار کرده و ایندکسهای منفی را به صورت شمارش از انتها تفسیر میکند.
شکل خلاصه .[x:] برشی از ایندکس x تا انتها ایجاد میکند، و شکل خلاصه .[:y] برشی از ابتدا تا ایندکس y (غیرشامل) ایجاد میکند. هنگامی که x برابر با null باشد، به عنوان 0 تفسیر میشود، و هنگامی که y برابر با null باشد، به عنوان length تفسیر میشود. بدین معنا که .[x:] معادل .[x:null] و .[:y] معادل .[null:y] است. برای نمونه:
- •
- "Hello World!" | .[ :5], .[6: ] --> "Hello" "World!"
- •
- "Hello World!" | .[null:5], .[6:null] --> "Hello" "World!"
- •
- "Hello World!" | .[null : null] --> "Hello World!"
این عملگر در صورتی مقداری برمیگرداند که ورودی آن یک رشته یا یک آرایه باشد، و ایندکسهای x و y آن یا null یا عدد صحیح باشند. در غیر این صورت، خطا صادر میشود. برای نمونه:
- •
- [] | try .["a":] catch "fail" --> "fail"
- •
- {} | try .[1:-1] catch "fail" --> "fail"
سازگاری
پیمایش / تکرار (.[])
عملگر پیمایش .[] هنگامی که آرایه [v1, ..., vn] یا شیء {k1: v1, ..., kn: vn} به آن داده شود، تمام مقادیر v1 ... vn را تولید میکند. برای نمونه: [1, 2, 3] | .[] --> 1 2 3 و {a: 1, b: 2} | .[] --> 1 2.
مسیرهای ترکیبی (Compound paths)
میتوانید هر تعداد عملگر مسیر را به یک فیلتر اتمیک الحاق کنید. به چنین توالیای از عملگرهای مسیر، یک مسیر ترکیبی (compound path) گفته میشود. برای نمونه، میتوانید explode.[] را به عنوان شکل خلاصه explode | .[] بنویسید.
اگر فیلتر اتمیک ابتدایی . (همانی) باشد، میتوانید آن را حذف کنید. برای نمونه، فیلترهای زیر همگی معادل یکدیگرند:
- •
- {a: [{b: 1}]} | . | .a | .[] | .b --> 1
- •
- {a: [{b: 1}]} | . .a .[] .b --> 1
- •
- {a: [{b: 1}]} | .a .[] .b --> 1
- •
- {a: [{b: 1}]} .a .[] .b --> 1
میتوانید . ابتدایی عملگرهای مسیری را که به شکل .[...] هستند حذف کنید، مگر اینکه آن عملگر مسیر در ابتدای مسیر ترکیبی باشد و فیلتر اتمیک ابتدایی . حذف شده باشد. برای نمونه، فیلترهای زیر همگی معادل یکدیگرند:
- •
- [[[1, 2, 3]]] | .[0] | .[] | .[:-1] --> [1, 2]
- •
- [[[1, 2, 3]]] | .[0] .[] .[:-1] --> [1, 2]
- •
- [[[1, 2, 3]]] | .[0] [] [:-1] --> [1, 2]
- •
- [[[1, 2, 3]]] [0] [] [:-1] --> [1, 2]
همچنین میتوانید هر عملگر مسیر را در این بخش با یک ? خاتمه دهید، که خطاهای ناشی از آن عملگر را فرونشانی و پنهان میکند. برای نمونه، .[] اگر ورودی نه آرایه باشد و نه شیء خطا میدهد، اما .[]? این خطاها را مهار کرده و به جای آن هیچ خروجیای تولید نمیکند. نمونهها:
- •
- [1, {a: 2 }, {a: 3 }] | .[].a? --> 2 3
- •
- [1, [2, 3 ], [4, 5 ]] | .[][]? --> 2 3 4 5
- •
- 1, {a: [2]}, {a: [3]} | .a?[] --> 2 3
عملگرهای بدون آرگومان (NULLARY)
همانی (.)
فیلتر همانی . ورودی خود را مستقیماً به خروجی منتقل میکند؛ برای نمونه: 1 --> 1.
بازگشت (..)
فیلتر بازگشتی .. ورودی خود و تمام زیرمقادیر موجود در آن را تولید میکند. این فیلتر معادل با recurse است. برای نمونه: [1, [2]] | .. --> [1, [2]] 1 [2] 2 و {a: {b: 1}} | .. --> { "a": { "b": 1 } } { "b": 1 } 1.
عملگرهای یکانی (UNARY)
نفی (-)
عملگر نفی -f ورودی را به فیلتر اتمیک f میدهد و حاصل منفی خروجیهای آن را برمیگرداند. برای نمونه: 1 | - . --> -1.
فرونشانی خطا (?)
عملگر فرونشانی خطا f? ورودی را به فیلتر اتمیک f میدهد و خروجیهای آن را برمیگرداند، اما هرگونه خطایی را که توسط f پرتاب شود نادیده میگیرد. این عملگر معادل try f catch empty است. برای نمونه: 1 | .a? --> (هیچ خروجیای تولید نمیشود).
عملگرهای دوتایی پیچیده (BINARY (COMPLEX))
عملگرهای دوتایی پیچیده دو فیلتر را به عنوان آرگومان دریافت میکنند و میتوانند تعداد خروجیهای تولیدشده را تغییر دهند.
ترکیب / لولهکشی (|)
عملگر لولهکشی f | g ورودی را به f میدهد، و سپس هر خروجی f را به نوبه خود به g میدهد. این کار خروجیهای g را بازمیگرداند. برای نمونه: [1, 2] | .[] | . + 1 --> 2 3.
الحاق (,)
عملگر الحاق f, g ورودی یکسان را به هر دو فیلتر f و g میدهد، و ابتدا خروجیهای f و سپس خروجیهای g را برمیگرداند. برای نمونه: 1 | ., . + 1 --> 1 2.
پیوند متغیر (as $x |)
دستور f as $x | g فیلتر f را روی ورودی اجرا میکند و به ازای هر خروجی f، متغیر $x را به آن مقدار مقید کرده و سپس g را با همان ورودی اولیه اجرا میکند. برای نمونه: 1 | (2, 3) as $x | . + $x --> 3 4.
انتساب ساده (=)
عملگر انتساب p = f مقادیر ارجاعشده توسط مسیر p را با خروجیهای فیلتر f جایگزین میکند. برای نمونه: {a: 1} | .a = 2 --> { "a": 2 }. برای جزئیات بیشتر بخش انتسابها را ببینید.
انتساب بهروزرسانی (|=)
عملگر p |= f مقادیر ارجاعشده توسط مسیر p را با عبور دادن آنها از فیلتر f بهروزرسانی میکند. برای نمونه: {a: 1} | .a |= . + 1 --> { "a": 2 }.
انتساب محاسباتی (+=، -=، …)
عملگرهای انتساب محاسباتی به شکل p o= f خلاصه عبارت p |= . o f هستند. عملگرهای معتبر عبارتند از: +=، -=، *=، /=، %= و //=. برای نمونه: {a: 1} | .a += 2 --> { "a": 3 }.
تناوب (//)
عملگر تناوب f // g فیلتر f را اجرا میکند. اگر f حداقل یک مقدار با ارزش بولی true (یعنی مقداری غیر از false یا null) تولید کند، تنها خروجیهای معتبر f بازگردانده میشوند. در غیر این صورت، خروجیهای g بازگردانده میشوند. برای نمونه: null // 1 --> 1 و false // 2 --> 2 و 1 // 2 --> 1.
منطق (or، and)
عملگرهای منطقی f or g و f and g محاسبات بولی را انجام میدهند. برای نمونه: true or false --> true و true and false --> false.
عملگرهای دوتایی ساده (BINARY (SIMPLE))
عملگرهای دوتایی ساده عملیات حسابی، مقایسهای و تساوی را انجام میدهند.
تساوی (==، !=)
عملگر f == g بررسی میکند که آیا مقادیر تولیدشده توسط f و g برابر هستند یا خیر. عملگر f != g برابری را نفی میکند. برای نمونه: 1 == 1 --> true و 1 != 2 --> true.
ترتیب و مقایسه (<، >، <=، >=)
عملگرهای مقایسهای ترتیب نسبی مقادیر را بررسی میکنند. ترتیب کلی انواع داده در jq به صورت زیر است: null < false < true < اعداد < رشتهها < آرایهها < اشیاء. برای نمونه: 1 < 2 --> true و null < 0 --> true و true < "a" --> true.
جمع و تفریق (+، -)
عملگرهای + و - برای عملیات حسابی روی اعداد، الحاق آرایهها و رشتهها و ادغام اشیاء استفاده میشوند. برای نمونه:
- •
- اعداد: 1 + 2 --> 3
- •
- آرایهها: [1] + [2] --> [1, 2]
- •
- رشتهها: "a" + "b" --> "ab"
- •
- اشیاء: {a: 1} + {b: 2} --> { "a": 1, "b": 2 }
ضرب و تقسیم (*، /)
عملگر * برای ضرب اعداد و ادغام بازگشتی اشیاء استفاده میشود. عملگر / برای تقسیم اعداد و تقسیم (split) رشتهها به کار میرود. برای نمونه:
- •
- ضرب اعداد: 2 * 3 --> 6
- •
- تقسیم اعداد: 6 / 2 --> 3.0
- •
- تقسیم رشته: "a,b" / "," --> ["a", "b"]
باقیمانده (%)
عملگر f % g باقیمانده تقسیم را برمیگرداند. برای نمونه: 10 % 3 --> 1.
کلمات کلیدی (KEYWORDS)
if-then-else
عبارت شرطی if p then f else g end ابتدا فیلتر شرطی p را اجرا میکند. اگر حاصل دارای ارزش بولی true باشد شاخه then، و در غیر این صورت شاخه else اجرا میشود. میتوان از شاخههای اختیاری elif نیز استفاده کرد. برای نمونه: if 1 > 0 then "yes" else "no" end --> "yes".
try-catch
عبارت try f catch g فیلتر f را اجرا میکند. اگر f خطایی صادر کند، خطا متوقف شده و شاخه catch با پیام خطا به عنوان ورودی اجرا میشود. بخش catch اختیاری است (که در صورت حذف، خطا را نادیده میگیرد). برای نمونه: try error("oops") catch . --> "oops".
label-break
ساختار label $out | ... break $out ... امکان خروج زودهنگام از حلقهها و جریانهای پردازش را فراهم میکند. برای نمونه: label $out | 1, 2, break $out, 3 --> 1 2.
reduce / foreach
عبارت reduce f as $x (init; update) روی تمام مقادیر تولیدشده توسط f پیمایش میکند و حالت را با مقدار اولیه init مقداردهی کرده و با هر عنصر با update بهروزرسانی مینماید، و در نهایت فقط حالت پایانی را برمیگرداند. عبارت foreach f as $x (init; update; extract) مشابه است، اما حالتهای میانی را نیز استخراج و تولید میکند. برای نمونه: reduce (1, 2, 3) as $x (0; . + $x) --> 6.
def
کلمه کلیدی def برای تعریف توابع سفارشی به کار میرود. نحو کلی آن به صورت def name(args): body; است. برای نمونه: def add_one: . + 1; 5 | add_one --> 6.
کتابخانه استاندارد (STANDARD LIBRARY)
این بخش تمامی فیلترهای نامگذاریشدهای را که به طور پیشفرض در هر ماژول jq در دسترس هستند فهرست میکند. این فیلترها به عنوان «توابع توکار» (builtins) نیز شناخته میشوند.
توابع پایه (BASIC)
error, error(f)
فیلتر error(f) به ازای هر خروجی f خطایی صادر میکند که خروجی به عنوان بار پیام خطا قرار میگیرد. فیلتر error معادل با error(.) است. همچنین امکان استفاده از error(f) در سمت چپ انتسابها نیز وجود دارد.
مثالها:
- •
- error(empty) --> (no output)
- •
- try error(41) catch (. + 1) --> 42
- •
- try (41 | error ) catch (. + 1) --> 42
- •
- try (error(41) = 1) catch (. + 1) --> 42
length
خروجی فیلتر length به نوع ورودی آن بستگی دارد:
- •
- مقدار null: برابر 0، یعنی null | length --> 0
- •
- بولی: خطا، یعنی true | try length catch "fail" --> "fail"
- •
- عدد: قدر مطلق عدد، یعنی -1, 1 | length --> 1 1
- •
- رشته متنی: تعداد کاراکترها، یعنی "ゼノギアス" | length --> 5
- •
- رشته بایتی: تعداد بایتها، یعنی "ゼノギアス" | tobytes | length --> 15
- •
- آرایه: تعداد مقادیر، یعنی [1, [2, 3], 4] | length --> 3
- •
- شیء: تعداد جفتهای کلید-مقدار، یعنی {a: 0, b: 1} | length --> 2
keys, keys_unsorted
فیلتر keys_unsorted آرایهای تولید میکند که در صورت شیء بودن ورودی حاوی تمام کلیدها و در صورت آرایه بودن ورودی حاوی تمام ایندکسها است. فیلتر keys معادل با keys_unsorted | sort است. برای نمونه:
- •
- {c: 1, b: 2, a: 1} | keys_unsorted --> ["c", "b", "a"]
- •
- {c: 1, b: 2, a: 1} | keys --> ["a", "b", "c"]
- •
- [1, 2, 3] | keys_unsorted --> [0, 1, 2]
- •
- [1, 2, 3] | keys --> [0, 1, 2]
to_entries, from_entries, with_entries(f)
فیلتر to_entries یک آرایه یا یک شیء را به عنوان ورودی دریافت میکند. آن را به آرایهای از اشیاء به شکل {key: k, value: v} تبدیل میکند، به گونهای که .[k] روی ورودی اصلی مقدار v را میدهد. For example:
- •
- [ 1, 2] | to_entries --> [{"key": 0 , "value": 1}, {"key": 1 , "value": 2}]
- •
- {a: 1, b: 2} | to_entries --> [{"key": "a", "value": 1}, {"key": "b", "value": 2}]
The filter from_entries constructs an object from an array of entries as given by to_entries. For example, {a: 1, b: 2} | to_entries | from_entries --> {"a": 1, "b": 2}.
The filter with_entries(f) is equivalent to to_entries | map(f) | from_entries. For example, {"a": 1, "b": 2} | with_entries(.key |= ascii_upcase) --> {"A": 1, "B": 2}
مصرفکنندگان جریان (STREAM CONSUMERS)
first, first(f), last, last(f)
The filter first(f) yields the first output of f if there is one, else nothing. For example, first(1, 2, 3) --> 1 and first(empty) --> (no output).
This filter stops evaluating f after the first output, meaning that it yields an output even if f yields infinitely many outputs. For example, first(repeat(0)) --> 0 and first(1, def f: f; f) --> 1.
Similarly, last(f) yields the last output of f if there is one, else nothing. If f yields an error, then the first error of f is yielded. For example, last(1, 2, 3) --> 3, last(empty) --> (no output), and try last(1, error("fail"), 3) catch . --> "fail".
The filters first and last are short forms for first(.[]) and last(.[]), respectively. You can use them to retrieve the first/last element of an array, such as [1, 2, 3] | first, last --> 1 3.
limit($n; f)
The filter limit($n; f) yields the first $n outputs of f. If $n <= 0, it yields no outputs. For example:
- •
- limit( 3; 1, 2 ) --> 1 2
- •
- limit( 3; 1, 2, 3, 4) --> 1 2 3
- •
- limit(-1; 1, 2 ) --> (no output)
سازگاری
skip($n; f)
The filter skip($n; f) yields all outputs after the first $n outputs of f. If $n <= 0, it yields all outputs of f. For example:
- •
- skip( 3; 1, 2 ) --> (no output)
- •
- skip( 1; 1, 2, 3, 4) --> 2 3 4
- •
- skip(-1; 1, 2 ) --> 1 2
nth($i), nth($i; f)
The filter nth($i; f) yields the $i-th output of f. If f yields less than $i outputs, then this filter yields no output. For example:
- •
- nth(0; 1, 2, 3) --> 1
- •
- nth(2; 1, 2, 3) --> 3
- •
- nth(3; 1, 2, 3) --> (no output)
The filter nth($i) is a short form for .[$i]; e.g. [1, 2, 3] | nth(0) --> 1.
isempty(f)
فیلتر isempty(f) در صورتی که f هیچ خروجیای تولید نکند true و در غیر این صورت false برمیگرداند. If the first output of f is an error, isempty(f) yields that error instead. For example:
- •
- isempty( empty) --> true
- •
- isempty(1, 2) --> false
- •
- isempty(1, error) --> false
- •
- try isempty(error, 1) catch "fail" --> "fail"
any, any(p), any(f; p)
The filter any(f; p) yields true if any output of f | p has the boolean value true, else false. For example:
- •
- any(0, 1, 2; . == 42) --> false
- •
- any(0, 1, 2; . == 42, . == 2) --> true
The filters any(p) and any are short forms of any(.[]; p) and any(.), respectively. For example:
- •
- [1, 2, 3] | any(. % 2 == 0) --> true
- •
- [false, true] | any --> true
all, all(p), all(f; p)
The filter all(f; p) yields true if all outputs of f | p have the boolean value true, else false. For example:
- •
- all(0, 1, 2; . > 0) --> false
- •
- all(0, 1, 2; . >= 0) --> true
The filters all(p) and all are defined analogously to any(p) and any.
add, add(f)
The filter add(f) yields the sum of all elements yielded by f, or null if f yields no outputs. For example:
- •
- add(1, 2, 3) --> 6
- •
- add(empty) --> null
The filter add is a short form of add(.[]). You can use it to add all values of an array or object:
- •
- [1, 2, 3] | add --> 6
- •
- {a: 1, b: 2} | add --> 3
تولیدکنندگان جریان (STREAM GENERATORS)
empty
The filter empty yields no output.
range($upto), range($from; $upto), range($from; $upto; $step)
The filter range($from; $upto; $step) adds $step to $from until it exceeds $upto. For example:
- •
- range(1; 9; 2) --> 1 3 5 7
- •
- range(1; 10; 2) --> 1 3 5 7 9
- •
- range(9; 1; -2) --> 9 7 5 3
- •
- range(9; 0; -2) --> 9 7 5 3 1
The filter range($from; $upto) is a short form of range($from; $upto; 1) and the filter range($upto) is a short form of range(0; $upto). For example:
- •
- range(5) --> 0 1 2 3 4
- •
- range(2; 5) --> 2 3 4
سازگاری
recurse, recurse(f)
The filter recurse(f) is equivalent to ., (f | recurse(f)). It first outputs its input, then runs f and recurse(f) on its outputs. This is useful to create infinite sequences. You can create a finite sequence by having f return empty, e.g. via select. For example:
- •
- 0 | limit(5; recurse(.+1)) --> 0 1 2 3 4
- •
- 0 | recurse(.+1 | select(. < 5)) --> 0 1 2 3 4
The filter recurse(f; p) is equivalent to recurse(f | select(p)). That means that it recurses only on outputs of f for which p yields a true output. For example:
- •
- 0 | recurse(.+1; . < 5) --> 0 1 2 3 4
The filter recurse is a short form for recurse(.[]?). It returns all values recursively contained in the input, e.g. [1, [2], {a: 3}] | recurse --> [1, [2], {"a": 3}] 1 [2] 2 {"a":3} 3. It yields the same outputs as .. (recursion).
repeat(f)
The filter repeat(f) runs f and yields its outputs over and over again. For example, 2 | limit(7; repeat(1, ., 3)) --> 1 2 3 1 2 3 1.
while(p; f), until(p; f)
The filter while(p; f) yields its input and applies f to it, while p returns true.
The filter until(p; f) applies f to its input until p returns true, at which point the filter returns its input.
Examples:
- •
- 0 | while(. <= 3; . + 1) --> 0 1 2 3
- •
- 0 | until(. >= 3; . + 1) --> 3
انتخاب و فیلترسازی (SELECTION)
The filters in this section classify their inputs or output them selectively.
select(p)
The filter select(p) yields its input for each true output of p. For example, (0, 1, -1, 2, -2) | select(. >= 0) --> 0 1 2.
nulls, booleans, numbers, strings, arrays, objects
Any of these filters yields its input if it is of the given type, else nothing. For example:
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | nulls --> null
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | booleans --> true
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | numbers --> 0
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | strings --> "Hi!"
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | arrays --> [1, 2]
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | objects --> {"a": 1}
isboolean, isnumber, isstring, isarray, isobject
For every filter in this section, like isboolean, …, isobject, there is a corresponding filter in the previous section like, booleans, …, objects. Any of these filters yields true if its corresponding filter in the previous section yields some output, else false. For example:
- •
- null | isboolean --> false, because null | booleans --> (no output).
- •
- true | isboolean --> true , because true | booleans --> true.
سازگاری
normals, finites
These filters return its input if isnormal or isfinite is true, respectively, else false.
values, iterables, scalars
The filter values yields its input if it is not null, else nothing.
If a value is either an array or an object, it is said to be iterable; otherwise, it is said to be scalar. (The iteration filter .[] succeeds on any iterable value, whereas it fails on any scalar.)
The filters iterables and scalars yield their input if it is iterable or scalar, respectively, else nothing.
Examples:
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | values --> true 0 "Hi!" [1, 2] {"a": 1}
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | scalars --> null true 0 "Hi!"
- •
- null, true, 0, "Hi!", [1, 2], {a: 1} | iterables --> [1, 2] {"a": 1}
isnan, isinfinite, isfinite, isnormal
The filter isnan yields true if its input is NaN, else false. Note that it is not equivalent to . == nan, because nan is not equal to itself; see equality.
The filter isinfinite yields true if its input is either Infinity or -Infinity, else false.
The filter isfinite yields true if its input is a number that is not infinite, else false.
The filter isnormal yields true if its input is a number that is neither 0, NaN, nor infinite.
Examples:
- •
- nan | isnan, isinfinite, isfinite, isnormal --> true false true false
- •
- infinite | isnan, isinfinite, isfinite, isnormal --> false true false false
- •
- 0 | isnan, isinfinite, isfinite, isnormal --> false false true false
- •
- 1 | isnan, isinfinite, isfinite, isnormal --> false false true true
type
The filter type returns the type of its input value as string. For example:
- •
- null | type --> "null"
- •
- false | type --> "boolean"
- •
- 0 | type --> "number"
- •
- "foo" | type --> "string"
- •
- [1] | type --> "array"
- •
- {} | type --> "object"
Note that both text strings and byte strings both have the same type "string".
عضویت و جستجو (MEMBERSHIP)
contains($x), inside($x)
The filter contains($x) yields true if any of the following conditions holds, else false.
- •
- The input is a string and $x is a substring of it.
- •
- The input is an array, $x is an array, and for every value v in $x, there is some value in the input that contains(v).
- •
- The input is an object, $x is an object, and for every key-value pair {k: v} in $x, there is a value for the key k in the input that contains(v).
- •
- The input is null, boolean, or a number, and $x is equal to the input.
Examples:
- •
- "Hello, world!" | contains("world") --> true
- •
- [1, 2, 3] | contains([1, 3]) --> true
- •
- [[1, 2], 3] | contains([3, [1]]) --> true
- •
- {a: 1, b: 2} | contains({a: 1}) --> true
- •
- {a: [1, 2]} | contains({a: [1]}) --> true
- •
- 0 | contains(0) --> true
The filter inside($x) is a flipped version of contains. For example, "world" | inside("Hello, world") --> true.
indices($x)
The filter indices($x) yields the following:
- •
- If the input and $x are either both strings or both arrays, then it yields the indices i for which .[i:][:$x | length] == $x; e.g. "Alice, Bob, and Carol" | indices(", ") --> [5, 10] and [0, 1, 2, 3, 1, 2, 3] | indices([1, 2]) --> [1, 4].
- •
- If the input is an array and $x is not an array, then it yields the indices i for which .[i] == $x; e.g. [0, 1, 2, 3, 1, 2, 3] | indices(1) --> [1, 4].
- •
- Otherwise, it yields an error.
This means that [[1, 2], 3] | indices([1, 2]) --> [], because the input array has neither 1 nor 2, just [1, 2] and 3.
index($x), rindex($x)
The filters index($x) and rindex($x) are shorthand for indices($x) | first and indices($x) | last, respectively. For example:
- •
- "Alice, Bob, and Carol" | index(", ") --> 5
- •
- [0, 1, 2, 3, 1, 2, 3] | rindex([1, 2]) --> 4
- •
- "Hello world!" | index(", ") --> null
- •
- [0, 1, 2] | rindex(3) --> null
has($k), in($x)
The filter has($k) yields true if .[$k] points to data in the input, else false. For example:
- •
- [1, 2, 3] | has( 0 , 3 ) --> true false
- •
- {a: 1, b: 2} | has("a", "c") --> true false
The filter in($x) is a flipped version of has, just like inside is a flipped version of contains. For example, "a" | in({a: 1, b: 2}) --> true.
سازگاری
- •
- [1, 2, 3] | has(-1, -2, -3) --> true true true (false false false in jq)
- •
- [1, 2, 3] | has({start: 1}) --> true (error in jq)
This difference arises because in jq, has($k) has a much weaker relationship with .[$k] than in jaq. The following properties hold in both jq and jaq:
- •
- [1, 2, 3] | .[-1, -2, -3] --> 3 2 1
- •
- [1, 2, 3] | .[{start: 1}] --> [2, 3]
بهروزرسانیها (UPDATES)
map(f), map_values(f)
The filter map(f) obtains all values of the input (via .[]), applies f to the values, and collects all results into an array. For example:
- •
- [1, 2, 3] | map(., .*2) --> [1, 2, 2, 4, 3, 6].
- •
- {a: 1, b: 2} | map(., .*2) --> [1, 2, 2, 4].
- •
- [1, 2, 3, 4] | map(select(. % 2 == 0)) --> [2, 4].
The filter map_values(f) has the same effect as map(f) when the input is an array, but when the input is an object, map_values(f) also outputs an object. For example:
- •
- [1, 2, 3, 4] | map_values(.*2) --> [2, 4, 6, 8]
- •
- {a: 1, b: 2} | map_values(.*2) --> {"a": 2, "b": 4}
walk(f)
The filter walk(f) recursively updates its input with f. For example:
- •
- [[1, 2], [3]] | walk(numbers += 1) --> [[2, 3], [4]]
del(f)
The filter del(f) deletes values at the locations given by f. It is equivalent to f |= empty. For example:
- •
- [1, 2, 3, 4] | del(.[] | select(. % 2 == 0)) --> [1, 3]
- •
- [1, 2, 3] | del(.[1]) --> [1, 3]
- •
- [1, 2, 3] | del(.[1:]) --> [1]
- •
- {a: 1, b: 2} | del(.a) --> {"b": 2}
مسیرها (PATHS)
path(f)
The filter path(f) records for each output of f its position in the input, and yields that position as a path. A path is an array that may contain indices or “slice objects”. The latter must contain a "start" and/or an "end" key with an integer value. For example:
- •
- [{a: 1}, {a: 2}] | path(.[].a) --> [0, "a"] [1, "a"]
- •
- [1, 2, 3] | path(.[1:][:-1]) --> [{"start": 1}, {"end": -1}]
- •
- [1, 2, 3] | path(.[1: -1]) --> [{"start": 1, "end": -1}]
If f returns values that do not point to the input, then path(f) yields an error.
- •
- try path(0 ) catch "fail" --> "fail"
- •
- try path(. as $x | $x) catch "fail" --> "fail"
paths, paths(p)
The filter paths yields the paths to all ancestor values of the input. It is equivalent to skip(1; path(..)).
The filter paths(p) yields the paths to all ancestor values of the input for which p yields true.
Examples:
- •
- [1, {a: 2}] | paths --> [0] [1] [1, "a"]
- •
- [1, {a: 2}] | paths(isnumber) --> [0] [1, "a"]
- •
- [1, {a: 2}] | paths(isobject) --> [1]
getpath($path)
The filter getpath($path) is the inverse filter for path(f). If path(f) yields no error, then getpath(path(f)) yields the same outputs as f. For example:
- •
- [{a: 1}, {a: 2}] | getpath([0, "a"], [1, "a"]) --> 1 2
- •
- [1, 2, 3] | getpath([{"start": 1}, {"end": -1}]) --> [2]
- •
- [1, 2, 3] | getpath([{"start": 1, "end": -1}]) --> [2]
setpath($path; $v)
فیلتر setpath($path; $v) مقدار موجود در مسیر $path را روی $v تنظیم میکند. It is equivalent to getpath($path) = $v. For example:
- •
- [[1, 2], [3, 4]] | setpath([1, 0]; 5) --> [[1, 2], [5, 4]]
delpaths($paths)
The filter delpaths($paths) takes an array of paths and deletes all corresponding values in the order given by the array. For example:
- •
- [1, 2, 3] | delpaths([[0]]) --> [2, 3]
- •
- [{a: 1, b: 2}, 3] | delpaths([[0, "b"], [1]]) --> [{"a": 1}]
سازگاری
To use delpaths in an interoperable fashion, use $paths such that:
- •
- Paths to descendants come before paths to their ancestors.
- •
- Paths to array elements to the right come before paths to elements to the left.
For example, .. returns ancestors before descendants and array elements to the left before elements to the right. To use the output of .. in delpaths, it suffices to reverse the order of its outputs:
- •
- ["a", 0, "b", 1] | delpaths([path(.. | strings)] | reverse) --> [0, 1 ] (right)
- •
- ["a", 0, "b", 1] | delpaths([path(.. | strings)] ) --> [0, "b"] (wrong)
pick(f)
The filter pick(f) constructs an object that contains only those parts of the input that f returns. For example:
- •
- {a: {b: 1, c: 2}, d: 3} | pick(. ) --> {"a": {"b": 1, "c": 2}, "d": 3}
- •
- {a: {b: 1, c: 2}, d: 3} | pick(.a.c ) --> {"a": { "c": 2} }
- •
- {a: {b: 1, c: 2}, d: 3} | pick(.a.c, .d) --> {"a": { "c": 2}, "d": 3}
سازگاری
- •
- [1, 2, 3] | pick(.[0 ]) yields [1]
- •
- [1, 2, 3] | pick(.[1 ]) yields [null, 2]
- •
- [1, 2, 3] | pick(.[1:]) yields [2, 3]
While implementing this functionality in jaq, I found many corner cases that would have made the proper documentation of this filter very complex. I also found a few surprising behaviours in jq, e.g. that [1, 2, 3] | pick(.[-1]) yields an error. In the end, I decided to support only the simpler and well-understandable subset of paths to objects.
بولیها (BOOLEANS)
true, false
The filters true and false yield the values true and false, respectively:
- •
- true --> true
- •
- false --> false
not
The filter not converts its input to its boolean value and returns its negation. For example:
- •
- true | not --> false
- •
- false | not --> true
- •
- "foo" | not --> false
toboolean
The filter toboolean is for booleans what tonumber is for numbers. For example:
- •
- true | toboolean --> true
- •
- "true" | toboolean --> true
- •
- "[true]" | try toboolean catch "fail" --> "fail"
اعداد (NUMBERS)
tonumber
The filter tonumber takes as input either a number or a string. If the input is a number, it is returned unchanged; if the input is a string, it is parsed to a number, failing if this does not succeed. For example:
- •
- 42 | tonumber --> 42
- •
- "42" | tonumber --> 42
- •
- "[42]" | try tonumber catch "fail" --> "fail"
infinite, nan
The filters infinite and nan yield the floating-point numbers Infinity and NaN:
- •
- infinite --> Infinity
- •
- nan | isnan --> true (we cannot test for equality with NaN here, because nan == nan --> false)
abs
The filter abs yields the negation of the input if the input is smaller than 0, else it yields the input. Note that due to this definition, strings, arrays, and objects are also returned unchanged, because they are larger than 0; see ordering.
Examples:
- •
- -2.0, -1, 0, 1, 2.0 | abs --> 2.0 1 0 1 2.0
- •
- "foo", [], {} | abs --> "foo" [] {}
floor, round, ceil
The filters floor, round and ceil round a number to its closest smaller integer, to its closest integer, and to its closest larger integer, respectively. For example:
- •
- 0.5 | floor, round, ceil --> 0 1 1
- •
- 0.4 | floor, round, ceil --> 0 0 1
- •
- 0.0 | floor, round, ceil --> 0 0 0
- •
- -0.4 | floor, round, ceil --> -1 0 0
- •
- -0.5 | floor, round, ceil --> -1 -1 0
- •
- 0, 1 | round --> 0 1
- •
- nan | round | isnan --> true
- •
- infinite | round --> Infinity
ریاضیات (Math)
jaq implements many mathematical functions via libm. If not specified otherwise, these filters take and return floating-point numbers.
Zero-argument filters:
- •
- acos
- •
- acosh
- •
- asin
- •
- asinh
- •
- atan
- •
- atanh
- •
- cbrt
- •
- cos
- •
- cosh
- •
- erf
- •
- erfc
- •
- exp
- •
- exp10
- •
- exp2
- •
- expm1
- •
- fabs
- •
- frexp, which returns pairs of (float, integer).
- •
- gamma
- •
- ilogb, which returns integers.
- •
- j0
- •
- j1
- •
- lgamma
- •
- log
- •
- log10
- •
- log1p
- •
- log2
- •
- logb
- •
- modf, which returns pairs of (float, float).
- •
- nearbyint
- •
- pow10
- •
- rint
- •
- significand
- •
- sin
- •
- sinh
- •
- sqrt
- •
- tan
- •
- tanh
- •
- tgamma
- •
- trunc
- •
- y0
- •
- y1
Two-argument filters that ignore .:
- •
- atan2
- •
- copysign
- •
- drem
- •
- fdim
- •
- fmax
- •
- fmin
- •
- fmod
- •
- hypot
- •
- jn, which takes an integer as first argument.
- •
- ldexp, which takes an integer as second argument.
- •
- nextafter
- •
- nexttoward
- •
- pow
- •
- remainder
- •
- scalb
- •
- scalbln, which takes as integer as second argument.
- •
- yn, which takes an integer as first argument.
Three-argument filters that ignore .:
- •
- fma
Examples:
- •
- (3.141592 | sin) < (-5 | pow10) --> true establishes that sin(pi) is smaller than 10^-5.
- •
- fmax(2; 3) --> 3.0
- •
- fma(2; 3; 4) --> 10.0
آرایهها (ARRAYS)
sort, sort_by(f)
فیلتر sort یک آرایه را دریافت کرده و عناصر آن را مرتب میکند. برای نمونه:
-
[true, 1, "abc", [1], {"a": 1}, null, false, 0, "ABC", [], {}] | sort --> [null, false, true, 0, 1, "ABC", "abc", [], [1], {}, {"a": 1}]
فیلتر sort_by(f) فیلتر f را برای هر مقدار در آرایه ورودی ارزیابی کرده، و مقادیر را بر اساس خروجی f مرتب میسازد. برای نمونه:
- •
- [0, 1, 2, 3] | sort_by(. % 2) --> [0, 2, 1, 3]
- •
- [{a: 1, b: 2}, {a: 0, b: 3}] | sort_by(. ) --> [{"a": 0, "b": 3}, {"a": 1, "b": 2}]
- •
- [{a: 1, b: 2}, {a: 0, b: 3}] | sort_by(.a) --> [{"a": 0, "b": 3}, {"a": 1, "b": 2}]
- •
- [{a: 1, b: 2}, {a: 0, b: 3}] | sort_by(.b) --> [{"a": 1, "b": 2}, {"a": 0, "b": 3}]
group_by(f)
فیلتر group_by(f) آرایه ورودی را بر اساس f مرتب کرده، سپس تمامی مقادیری را که f برای آنها خروجیهای یکسانی تولید کرده در یک آرایه گروهبندی میکند. برای نمونه:
- •
- ["foo", "", "bar", "quux", "baz"] | group_by(length) --> [[""], ["foo", "bar", "baz"], ["quux"]]
- •
- [1, 2, 3, 4] | group_by(. % 2) --> [[2, 4], [1, 3]]
unique, unique_by(f)
فیلتر unique_by(f) آرایه ورودی را بر اساس f مرتب میکند. اگر f برای چندین مقدار در آرایه خروجیهای یکسانی تولید کند، تنها اولین مقدار نگه داشته میشود. برای نمونه:
- •
- ["foo", "", "bar", "quux", "baz"] | unique_by(length) --> ["", "foo", "quux"]
- •
- [1, 2, 3, 4] | unique_by(. % 2) --> [2, 1]
فیلتر unique معادل با unique_by(.) است. این فیلتر آرایه ورودی را مرتب کرده و تمام عناصر تکراری را حذف میکند؛ مثلاً: [3, 2, 1, 3, 4] | unique --> [1, 2, 3, 4].
min, max, min_by(f), max_by(f)
فیلترهای min و max به ترتیب کوچکترین و بزرگترین عنصر یک آرایه را تولید میکنند. برای نمونه:
- •
- [1, 2, 3] | min --> 1
- •
- [1, 2, 3] | max --> 3
The filters min_by(f) and max_by(f) evaluate the filter f for each value in the input array, and yield the value for which f produces the smallest or largest output, respectively. For example:
- •
- ["abc", [1, 2], {"a": 1}] | min_by(length) --> {"a": 1}
- •
- ["abc", [1, 2], {"a": 1}] | max_by(length) --> "abc"
You can yield multiple values in f to break ties such as:
- •
- ["abc", [1, 2], {"a": 1, "b": 3}] | min_by(length, add?) --> [1, 2]
reverse
The filter reverse takes an array and reverses it. For example, [1, 2, 3] | reverse --> [3, 2, 1].
transpose
The filter transpose takes an array of arrays and yields its transposition.
Examples:
- •
- [[1 , 2, 3], [4, 5, 6]] | transpose --> [[1, 4], [2, 5], [3, 6]]
- •
- [[1], [2, 3], [4, 5, 6]] | transpose --> [[1, 2, 4], [null, 3, 5], [null, null, 6]]
flatten, flatten($depth)
The filter flatten flattens input arrays, and the filter flatten($depth) flattens input arrays up to a certain depth. For example:
- •
- [1, [2, [3]], {a: [1, [2]]}] | flatten --> [1, 2, 3 , {"a": [1, [2]]}]
- •
- [1, [2, [3]], {a: [1, [2]]}] | flatten(0) --> [1, [2, [3]], {"a": [1, [2]]}]
- •
- [1, [2, [3]], {a: [1, [2]]}] | flatten(1) --> [1, 2, [3] , {"a": [1, [2]]}]
- •
- [1, [2, [3]], {a: [1, [2]]}] | flatten(2) --> [1, 2, 3 , {"a": [1, [2]]}]
- •
- null, true, 0, "Hi" | flatten --> [null] [true] [0] ["Hi"]
Note that flatten does not impact arrays that are descendants of an object.
bsearch($x)
The filter bsearch($x) takes a sorted array and performs a binary search for $x in the array. If the array contains $x, then the filter yields a positive $i such that .[$i] == $x; otherwise, the filter yields a negative $i such that inserting $x at the index -$i-1 in the array would preserve its the ordering.
Examples:
- •
- [0, 4, 8] | bsearch(8, 4, 0) --> 2 1 0
- •
- [0, 4, 8] | bsearch(-2, 2, 6, 10) --> -1 -2 -3 -4
If the input array is not sorted, then the output of this filter is meaningless.
combinations, combinations($n)
The filter combinations takes as input an array of arrays, such as [a1, ..., an]. It yields all combinations of the elements of the arrays a1 to an:
-
[[1, 2], [3, 4]] | combinations --> [1, 3] [1, 4] [2, 3] [2, 4]
When the input array is empty, combinations yields an empty array; [] | combinations --> [].
The filter combinations($n) takes a single array a1 as input. Its output is equivalent to [a1, ..., a1] | combinations, where a1 is repeated $n times.
-
[1, 2] | combinations(2) --> [1, 1] [1, 2] [2, 1] [2, 2]
رشتههای متنی (TEXT STRINGS)
Unless stated otherwise, all filters in this section take a text string as input, and fail if the input is of any other type.
tostring
فیلتر tostring ورودی خود را به یک رشته تبدیل میکند. Its output depends on the type of its input:
- •
- Text strings are returned unchanged, i.e. "Hi" | tostring --> "Hi".
- •
- Byte strings are interpreted as text string, i.e. "Hi" | tobytes | tostring --> "Hi". This takes constant time.
- •
- Any other value is formatted compactly as if output by jaq -c. For example:
- •
- null | tostring --> "null",
- •
- [0, 1] | tostring --> "[0,1]",
- •
- {a: 1} | tostring --> "{\"a\":1}".
utf8bytelength
The filter utf8bytelength yields the number of bytes of the input string. It is equivalent to tobytes | length, but different from length, which counts the number of characters.
For example, "ゼノギアス" | length, utf8bytelength, (tobytes | length) --> 5 15 15.
startswith($s), endswith($s)
The filter startswith($s) yields true if the input string starts with the string $s, else false. Similar for endswith($s). For example:
- •
- "ゼノギアス" | startswith("ゼノ") --> true
- •
- "ゼノギアス" | endswith("ギアス") --> true
trim, ltrim, rtrim
The filters ltrim and rtrim remove from the input string all leading and trailing whitespace, respectively. Here, whitespace corresponds to the White_Space Unicode property. The filter trim is equivalent to ltrim | rtrim.
For example:
- •
- " \t\n Bonjour ! \r " | ltrim --> "Bonjour ! \r "
- •
- " \t\n Bonjour ! \r " | rtrim --> " \t\n Bonjour !"
Note that there are a few quite unusual whitespace characters in this string.
ltrimstr($s), rtrimstr($s)
The filters ltrimstr($s) and rtrimstr($s) remove a single occurrence of $s from the start or the end of the string, respectively. If there is no such occurrence, the original string is returned. For example:
- •
- "foofoobar" | ltrimstr("foo") --> "foobar"
- •
- "foobarbar" | rtrimstr("bar") --> "foobar"
explode, implode
The filter explode yields an array containing a positive number for each valid Unicode code point of the input string and a negative number for each byte of each invalid Unicode code unit. For example:
-
"Dear ☀️" + (255 | tobytes | tostring) | explode --> [68,101,97,114,32,9728,65039,-255]
Here, we can see that "☀️" has turned into two code points, namely 9728 and 65039, whereas the invalid FF byte (= 255) has become -255.
The inverse filter of explode is implode:
-
[68,101,97,114,32,9728,65039, -255] | implode[:-1] --> "Dear ☀️"
(I omitted the FF byte at the end, because it is hard to save in a text editor.)
سازگاری
split($s)
This filter yields . / $s if its input . and $s are both strings, else it fails. See the section on division for details.
Note that there is also split($re; $flags) that splits by a regex.
join($s)
The filter join($s) takes as input an array [x1, ..., xn] and yields "" if the array is empty, otherwise "\(x1)" + $s + ... + $s + "\(xn)". That is, it concatenates the string representations of the array values interspersed with $s.
For example, to memorise the hierarchy of values in jq: ["null", "boolean", "number", "string", "array", "object"] | join(" < ") --> "null < boolean < number < string < array < object".
سازگاری
ascii_downcase, ascii_upcase
The filters ascii_downcase and ascii_upcase convert all ASCII letters in the input string to their lower/upper case variants, respectively. For example:
- •
- "Der λΠ-Kalkül" | ascii_downcase --> "der λΠ-kalkül"
- •
- "Der λΠ-Kalkül" | ascii_upcase --> "DER λΠ-KALKüL"
قالببندی رشتههای متنی (TEXT STRING FORMATTING)
The filters in this section can be prefixed to strings to influence string interpolation. However, these filters can also be used outside of string interpolation. For example:
- •
- "1 + 2 * 3" | @uri "https://duckduckgo.com/?q=\(.)" --> "https://duckduckgo.com/?q=1%20%2B%202%20%2A%203"
- •
- "1 + 2 * 3" | @uri --> "1%20%2B%202%20%2A%203"
If not indicated otherwise, all filters in this section convert their input to a string with tostring and yield a single string output. As result, byte strings are treated like equivalent text strings; e.g. "Hello world!" | (tobytes | @base64) == @base64 --> true.
سازگاری
@text
The filter @text is equivalent to tostring.
@json
The filter @json is equivalent to tojson.
@csv, @tsv
The filters @csv and @tsv are equivalent to tocsv and totsv, respectively.
@html, @htmld
The filter @html escapes a string so that it can be embedded in an HTML document. It replaces the following characters by HTML equivalents:
- •
- "<" | @html --> "<"
- •
- ">" | @html --> ">"
- •
- "&" | @html --> "&"
- •
- "'" | @html --> "'"
- •
- "\"" | @html --> """
The filter @htmld reverses the effect of @html. For example:
-
"\"1 < 2 & 2 > 1\", that's what he said." | @html | ., @htmld --> ""1 < 2 & 2 > 1", that's what he said." "\"1 < 2 & 2 > 1\", that's what he said."
سازگاری
@base64, @base64d
The filter @base64 Base64-encodes its input. The filter @base64d reverses this operation. For example:
-
"Hello world!" | @base64 | ., @base64d --> "SGVsbG8gd29ybGQh" "Hello world!"
سازگاری
@uri, @urid
The filter @uri applies percent-encoding to encode arbitrary data in a uniform resource identifier (URI). The filter @urid reverses this encoding. For example:
- •
- "Hello, World!" | @uri --> "Hello%2C%20World%21"
- •
- "Hello, World!" | @uri | @urid --> "Hello, World!"
@sh
The filter @sh escapes data for constructing Unix command-line prompts. It performs different things depending on its input type:
- •
- null, boolean, number: Convert to string via tostring.
- •
- String: Replace occurrences of ' by '\'' and surround by '.
- •
- Array of scalars: Apply @sh to elements and join the results with " " as separator.
- •
- Fail for any other type of value.
Examples:
- •
- null, true, 1 | @sh --> "null" "true" "1"
- •
- "It's green!" | @sh --> "'It'\\''s green!'"
- •
- ["jaq", "-n", "--arg", "slogan", "It's green!", "$slogan"] | @sh --> "'jaq' '-n' '--arg' 'slogan' 'It'\\''s green!' '$slogan'"
رشتههای بایتی (BYTE STRINGS)
tobytes
The filter tobytes converts its input to a byte string. Its output depends on the type of input:
- •
- Natural number in the range 0 to 255 (0xFF): Yields a byte string with a single byte, e.g. 0 | tobytes --> b"\x00".
- •
- Text string: Yields a byte string containing the underlying bytes of the text string, e.g. "Hi" | tobytes --> b"Hi". This takes constant time.
- •
- Byte string: Yields the byte string unchanged.
- •
- Array: Converts each element to a byte string and yields their concatenation, e.g. [0, "Hi", [1, 255]] | tobytes --> b"\x00Hi\x01\xFF". This is equivalent to map(tobytes) | add.
- •
- Anything else: Yields an error.
This is inspired by Erlang’s iolist_to_binary function.
سازگاری
سریالسازی و واسریالسازی (SERIALISATION & DESERIALISATION)
The filters in this section read and write data in all formats supported by jaq. See the formats section for general information about how jaq interprets these formats.
سازگاری
fromjson, tojson
The filter fromjson takes a string as input, parses it to JSON values and yields them. For example:
-
"null true 0 \"foo\" [1] {\"foo\": 1}" | fromjson --> null true 0 "foo" [1] { "foo" : 1}
The filter tojson takes an arbitrary value and outputs a string containing its JSON representation. For example:
-
[null,true,0, "foo" ,[1],{ "foo" :1}] | tojson --> "[null,true,0,\"foo\",[1],{\"foo\":1}]"
Note that tojson behaves similarly to tostring, but when its input is a string, it will also encode it to JSON, instead of returning it unchanged; i.e. "Hi" | tojson --> "\"Hi\"".
سازگاری
fromyaml, toyaml
The filter fromyaml takes a text string and parses it as sequence of YAML documents. It can yield an arbitrary number of outputs. For example:
- •
- "---\n 1 \n...\n---\n 2 \n..." | fromyaml --> 1 2
- •
- "" | fromyaml --> (no output)
The filter toyaml always yields exactly one output, namely a text string containing the current value encoded as YAML.
fromcbor, tocbor
The filter fromcbor takes a byte string and parses it as sequence of CBOR values. For example:
-
[0, 1, 32, 64, 96, 128, 160, 244, 245, 246] | tobytes | fromcbor --> 0 1 -1 b"" "" [] {} false true null
The filter tocbor always yields exactly one output, namely a byte string containing the current value encoded as CBOR.
fromtoml, totoml
The filter fromtoml takes a text string and parses it as a single TOML document. It yields always one output, because every TOML document encodes exactly one value. For example:
-
" [database]\n enabled = true\n ports = [ 8000, 8001, 8002 ]\n data = [ [\"delta\", \"phi\"], [3.14] ]\n temp_targets = { cpu = 79.5, case = 72.0 }\n " | fromtoml --> {"database": { "enabled": true, "ports": [8000,8001,8002], "data": [["delta", "phi"], [3.14]], "temp_targets": {"cpu": 79.5, "case": 72.0} }}
The filter totoml fails if the input is not an object or the input contains any jaq value not supported by TOML. It converts invalid UTF-8 sequences like CBOR.
fromxml, toxml
The filter fromxml takes a text string and parses it as sequence of XML tags. For example:
-
"<?xml version='1.0'?> <html> <body xmlns='http://www.w3.org/1999/xhtml'> Hello HTML! </body> </html>" | fromxml --> {"xmldecl": {"version": "1.0"}} {"t": "html","c": [{ "t": "body", "a": {"xmlns": "http://www.w3.org/1999/xhtml"}, "c": ["Hello HTML!"] }]}
The filter toxml takes data produced in the format by fromxml and yields a corresponding text string.
-
[ {"xmldecl": {"version": "1.0"}}, {"t": "html","c": [{ "t": "body", "a": {"xmlns": "http://www.w3.org/1999/xhtml"}, "c": ["Hello HTML!"] }]} ] | toxml --> "<?xml version=\"1.0\"?> <html> <body xmlns=\"http://www.w3.org/1999/xhtml\"> Hello HTML! </body> </html>"
fromcsv, tocsv
The filter fromcsv takes a string and yields each CSV row as an array of values. For example, "1,2\n3,4\n" | fromcsv --> [1,2] [3,4].
Strings can be written quoted or unquoted. Inside quoted strings, "" denotes a single quote (").
- •
- "plain,with space" | fromcsv --> ["plain","with space"]
- •
- "\"with,comma\"" | fromcsv --> ["with,comma"]
- •
- "\"with\nnewline\"" | fromcsv --> ["with\nnewline"]
- •
- "\"with\"\"quote\"" | fromcsv --> ["with\"quote"]
The booleans true and false are returned as such. An empty field is returned as null:
- •
- "true,,false" | fromcsv --> [true, null, false]
Any field that is a valid XJON number is returned as such:
-
"0,1,3.14,Infinity,+Infinity,-Infinity" | fromcsv --> [0,1,3.14,Infinity, Infinity,-Infinity]
Quoted booleans, numbers, or empty strings are always returned as strings:
"\"true\",\"\",\"false\",\"3.14\"" | fromcsv --> ["true", "", "false", "3.14"]
Rows can be terminated with LF ("\n") or CRLF ("\r\n"). The last row may or may not be terminated with LF/CRLF.
- •
- "1,2\r\n3,4\n" | fromcsv --> [1,2] [3,4]
- •
- "1,2\n3,4\r\n" | fromcsv --> [1,2] [3,4]
- •
- "1,2\n3,4" | fromcsv --> [1,2] [3,4]
A few extreme cases:
- •
- "" | fromcsv --> (no output)
- •
- "\n" | fromcsv --> [null] (empty lines are interpreted as null)
- •
- "," | fromcsv --> [null, null]
The filter tocsv takes an array of scalars. It transforms each array element depending on its type:
- •
- null: Yield "".
- •
- Boolean, number: Transform it via tostring.
- •
- String: Replace occurrences of " by "" and surround by ".
- •
- Fail for any other type of value.
Finally, the filter joins the transformed elements with "," as separator. For example:
-
[true, null, false, 1, "Give me \"quotes\", or die"] | tocsv --> "true,,false,1,\"Give me \"\"quotes\"\", or die\""
fromtsv, totsv
The filter fromtsv takes a string and yields each TSV row as an array of values. For example, "1\t2\n3\t4\n" | fromtsv --> [1,2] [3,4].
The filter totsv is similar to tocsv, with the following differences:
- •
- In strings, the characters '\n', '\r', '\t', '\', and '\u0000' are replaced by strings "\\n", "\\r", "\\t", "\\\\", "\\0". (In raw output, these look like \n, \r, \t, \\, and \0.) The result is not surrounded by ".
- •
- The transformed elements are joined with \t (tabulator).
For example:
-
[true, null, false, 1, "hello \"quotes\" and \n\r\t\\ escapes"] | totsv --> "true\t\tfalse\t1\thello \"quotes\" and \\n\\r\\t\\\\ escapes"
TSV strings are always unquoted. Encode control characters using \0, \t, \r, \n, and \\:
- •
- "with\\0NUL" | fromtsv --> ["with\u0000NUL"]
- •
- "with\\tTAB" | fromtsv --> ["with\tTAB"]
- •
- "with\\rCR" | fromtsv --> ["with\rCR"]
- •
- "with\\nLF" | fromtsv --> ["with\nLF"]
- •
- "with\\\\" | fromtsv --> ["with\\"]
تاریخ و زمان (DATE & TIME)
The filters in this section serve to convert between different time formats, such as:
- •
- Unix epoch: Marks a point in time by the number of seconds passed since January 1, 1970 00:00:00 (UTC). You can obtain the current time as Unix epoch via now.
- •
- ISO-8601 datetime string: Represents a date, a time, and a time zone as a string, such as "1970-01-01T00:00:00Z" (corresponding to Unix epoch 0).
- •
- “Broken down time” (BDT) array: Represents a date and a time as an array of the shape [year, month, day, hour, minute, second, weekday, yearday]. All components are integers, except for second, which may be a floating-point number. The month is counted from 0, the weekday is counted from Sunday (which is 0), and the yearday is the day in the year counted from 0. When a BDT array is used as input, only the first six components are considered.
You can convert between these representations via:
- •
- Unix epoch from/to ISO 8601: fromdate, todate
- •
- BDT to Unix epoch: mktime
- •
- Unix epoch to BDT: gmtime, localtime
- •
- Unix epoch or BDT from/to custom string: strptime, strftime, strflocaltime
As example, let us consider the time where the Hill Valley courthouse’s clock tower was struck by lightning, namely Saturday, November 12, 1955, at 10:04 p.m. PST. The corresponding date can be written in ISO 8601 as "1955-11-12T10:04:00-08:00". We can convert that to a Unix epoch and from there to a (UTC) BDT via:
-
"1955-11-12T22:04:00-08:00" | fromdate | gmtime --> [ 1955, 10, 13, 6, 4, 0, 0, 316 ]
We can infer that at this moment, in UTC it was November 13 (the BDT month is 10 and not 11, because BDT months are counted from 0), at 06:04:00. Furthermore, that day was a Sunday (because the weekday is 0), which was the 316-th day of the year (where 0 is the first day).
سازگاری
fromdate, todate, fromdateiso8601, todateiso8601
These filters convert between Unix time and ISO-8601 timestamps.
For example, the Apollo 13 accident happened at 03:08 UTC on April 14, 1970. Its corresponding Unix time is "1970-04-14T03:08:00Z" | fromdate --> 8910480. We can get back the ISO-8601 timestamp via 8910480 | todate --> "1970-04-14T03:08:00Z".
These filters can handle floating-point numbers, e.g. 0.123456 | todate --> "1970-01-01T00:00:00.123456Z" and "1970-01-01T00:00:00.123456Z" | fromdate --> 0.123456. In particular, fromdate yields a floating-point number if the time cannot be represented losslessly as an integer.
The filters fromdateiso8601 and todateiso8601 are synonyms of fromdate and todate, respectively.
strftime($fmt), strflocaltime($fmt)
The filters strftime($fmt) and strflocaltime($fmt) take as input either a number that is interpreted as Unix epoch, or a BDT array. The filters yield a string representation of the input time, using the format $fmt.
If the input is a Unix epoch, both strftime and strflocaltime interpret it as UTC timestamp. If the input is a BDT array, then strftime interprets input as UTC and strflocaltime interprets input as user local time. strftime outputs the time as UTC and strflocaltime outputs the time as user local time.
For example, if the user is in the CET zone (+0100):
- •
- 0 | strftime("%T %z (%Z)") --> "00:00:00 +0000 (UTC)"
- •
- [1970, 0, 1, 0, 0, 0] | strftime("%T %z (%Z)") --> "00:00:00 +0000 (UTC)"
- •
- 0 | strflocaltime("%T %z (%Z)") yields "01:00:00 +0100 (CET)"
- •
- [1970, 0, 1, 0, 0, 0] | strflocaltime("%T %z (%Z)") yields "00:00:00 +0100 (CET)"
سازگاری
strptime($fmt)
The filter strptime($fmt) takes a string and parses it using the format $fmt, yielding a BDT array. If no time zone is inferred from the input (e.g. via %Z), it is assumed to be UTC. For example:
- •
- "1970-01-01 00:00:00" | strptime("%F %T") --> [1970, 0, 1, 0, 0, 0, 4, 0]
- •
- "1970-01-01 00:00:00 Europe/Vienna" | strptime("%F %T %Q") --> [1970, 0, 1, 0, 0, 0, 4, 0]
gmtime, localtime
The filters gmtime and localtime take a Unix epoch as input and yield a corresponding BDT array, containing the time in UTC (gmtime) or in the user local time (localtime).
For example, if the user is in the CET zone (+0100):
- •
- 0 | gmtime --> [1970, 0, 1, 0, 0, 0, 4, 0]
- •
- 0 | localtime yields [1970, 0, 1, 1, 0, 0, 4, 0]
mktime
The filter mktime takes a BDT array that is assumed to be in UTC, and yields the corresponding Unix epoch. For example, [1970, 0, 1, 0, 0, 0] | mktime --> 0.
عبارتهای منظم (REGULAR EXPRESSIONS)
All the filters in this section, such as test, take a string as input and fail if they receive any other type of value. Furthermore, they all take two string arguments, namely the regular expression $re and the $flags that determine how the regular expression is interpreted. Omitting $flags is equivalent to passing "" as $flags. For example, test($re) is equivalent to test($re; "").
The supported flags are:
- •
- g: global search
- •
- n: ignore empty matches
- •
- i: case-insensitive
- •
- m: multi-line mode: ^ and $ match begin/end of line
- •
- s: single-line mode: allow . to match \n
- •
- l: greedy
- •
- x: extended mode: ignore whitespace and allow line comments (starting with #)
سازگاری
test
The filter test yields true if some part of the input matches the regular expression, else false. For example:
- •
- "jaq v3.0" | test("v[0-9]+\\.[0-9]+") --> true
- •
- "jaq V3.0" | test("v[0-9]+\\.[0-9]+") --> false
- •
- "jaq V3.0" | test("v[0-9]+\\.[0-9]+"; "i") --> true
scan
The filter scan yields all parts of the input that match the regular expression. For example:
- •
- "v2.0, v3.0" | scan("v[0-9]+\\.[0-9]+" ) --> "v2.0"
- •
- "v2.0, v3.0" | scan("v[0-9]+\\.[0-9]+"; "g") --> "v2.0" "v3.0"
- •
- "V2.0" | scan("v[0-9]+\\.[0-9]+") --> (no output)
match
The filter match yields an object for every part of the input that matches the regular expression, containing:
- •
- "offset": the character index of the start of the match
- •
- "length": the number of characters of the match
- •
- "string": the contents of the match
- •
- "captures": an array with an object for every capture group, containing:
- •
- "offset",
- •
- "length",
- •
- "string": as above, but for the capture group instead of the whole match
- •
- "name": the name of the capture group if it has one, else this key is omitted
Example:
-
"v2.0, v3.0" | match("v(?<maj>[0-9]+)\\.([0-9]+)"; "g") --> { "offset": 0, "length": 4, "string": "v2.0", "captures": [ { "offset": 1, "length": 1, "string": "2", "name": "maj" }, { "offset": 3, "length": 1, "string": "0" } ] } { "offset": 6, "length": 4, "string": "v3.0", "captures": [ { "offset": 7, "length": 1, "string": "3", "name": "maj" }, { "offset": 9, "length": 1, "string": "0" } ] }
capture
The filter capture yields an object for every part of the input that matches the regular expression, containing for each named capture group an entry with the group name as key and its matched string as value.
Example:
-
"v2.0, v3.0" | capture("v(?<maj>[0-9]+)\\.(?<min>[0-9]+)"; "g") --> { "maj": "2", "min": "0" } { "maj": "3", "min": "0" }
split, splits
The filter split($re; $flags) yields an array of those parts of the input string that do not match the regular expression $re. For example:
- •
- "Here be\tspaces" | split("\\s" ; "") --> ["Here", "be", "spaces"]
- •
- " More\n\n" | split("\\s+"; "") --> ["", "More", ""]
- •
- "" | split("\\s" ; "") --> [""]
Note that split($re; $flags) is equivalent to split($re; "g" + $flags), meaning that the string is split not only by the first match, but by all matches. Furthermore, unlike all other filters in this section, split($s) is not equivalent to split($s; $flags), because split($s) splits a string by a separator that is not interpreted as regular expression; see split.
The filter splits($re; $flags) yields the elements of the array yielded by split($re; $flags). For example, "Here be\tspaces" | splits("\\s") --> "Here" "be" "spaces". The filter splits($re) is equivalent to splits($re; "").
sub, gsub
The filter sub($re; f; $flags) replaces all parts of the input string that match $re by the output of f. Here, f receives an object as returned by capture; that is, for every named capture group, it contains its name as key and its matched string as value.
For example:
-
"Mr. 高橋 & Mrs. 嵯峨" | sub("(?<title>(Mr|Ms|Mrs)\\.) (?<name>\\S+)"; "\(.name) (\(.title))"; "g") --> "高橋 (Mr.) & 嵯峨 (Mrs.)"
When the filter f yields multiple outputs, then all potential combinations are output. For example:
-
"Thanks, fine." | sub("(?<word>\\w+)"; .word, (.word | ascii_upcase); "g") --> "Thanks, fine." "Thanks, FINE." "THANKS, fine." "THANKS, FINE."
We have following short forms:
- •
- The filter gsub($re; f; $flags) is equivalent to sub($re; f; "g" + $flags).
- •
- The filter gsub($re; f) is equivalent to gsub($re; f; "").
- •
- The filter sub($re; f) is equivalent to sub($re; f; "").
ورودی/خروجی (I/O)
This section contains filters that interact with the system. These filters may yield different outputs when given equal inputs.
input, inputs
The filter inputs yields all the inputs in the current input file. For example:
-
$ echo 1 2 3 | jaq -nc '[inputs]' [1,2,3]
This can be useful to fold over large (potentially infinite) amounts of values; for example, to create a cumulative sum over all input integers, you can use jaq -n 'foreach inputs as $x (0; .+$x)'.
The filter input yields the next input in the current input file.
سازگاری
debug, debug(f)
The filter debug(f) prints a debug message for every output of f to the standard error stream (stderr), then yields its input. For example:
-
$ jaq -n '0 | debug(1, 2)' ["DEBUG:", 1] ["DEBUG:", 2] 0
The filter debug is equivalent to debug(.).
stderr
The filter stderr prints its input to the standard error stream in raw and compact mode without newline. It then yields its input.
-
$ jaq -n '"Hi" | stderr' Hi"Hi"
halt, halt($exit_code)
The filter halt($exit_code) terminates jaq with the given exit code.
The filter halt terminates jaq with exit code 0.
-
$ jaq -n '1, halt , 2' 1 $ jaq -n '1, halt(42), 2'; echo $? 1 42
سازگاری
halt_error, halt_error($exit_code)
The filter halt_error($exit_code) prints its input via stderr. It then quits the jaq process with the given exit code.
The filter halt_error is equivalent to halt_error(5).
-
$ jaq -n '"Hi!" | halt_error(42)'; echo $? Hi!42 $ jaq -n '"Hi!" | halt_error '; echo $? Hi!5
سازگاری
now
This filter yields the Unix epoch as floating-point number.
-
$ date +%s | jaq 'now - . as $dt | 0 <= $dt and $dt <= 2' true
$ENV, env
The variable $ENV holds an object that contains an entry for every environment variable, where the key is the name of the variable and the value is its value. For example, {"EDITOR": "vim", "SHELL": "/usr/bin/bash"}.
-
$ MYFLAG=42 jaq -n '$ENV.MYFLAG' "42"
The filter env is equivalent to $ENV.
input_filename
The filter input_filename returns the name of the file currently read, or "<stdin>" if standard input is currently read.
-
$ jaq --slurp 'input_filename' "examples/benches.json" "examples/benches.json" $ echo 0 | jaq 'input_filename' "<stdin>"
سازگاری
-
$ echo 0 | jaq --null-input '(., inputs) | input_filename' "<stdin>" "<stdin>"
Here, jq yields null "<stdin>". Why? When executing the filter ., jq did not read any input, so input_filename returns null. However, when executing inputs, jq starts reading from standard input, so input_filename returns "<stdin>".
In contrast, jaq considers a file as being read even when it has only been opened.
repl
The filter repl starts an interactive REPL (read-eval-print-loop). You can nest calls to repl; that is, you can run repl from inside a repl. Use CTRL-D to exit a REPL.
This filter yields no output. It writes its history to a file jaq-history in the user’s cache directory.
سازگاری
jq does not have this filter.
پشتیبانینشده (UNSUPPORTED)
This section lists filters present in jq, but not in jaq.
- •
- $__loc__
- •
- modulemeta
- •
- have_literal_numbers
- •
- have_decnum
- •
- $JQ_BUILD_CONFIGURATION
- •
- builtins
- •
- input_line_number
jaq supports none of jq’s SQL-style operators, mostly for aesthetic reasons (uppercase-names) and because jq is not SQL:
- •
- INDEX
- •
- JOIN
- •
- IN
jaq does not support jq’s --stream option; therefore, it also does not implement the related filters:
- •
- truncate_stream
- •
- fromstream
- •
- tostream
ویژگیهای پیشرفته (ADVANCED FEATURES)
انتسابها (ASSIGNMENTS)
دستور jq امکان انتسابهایی به شکل p |= f را فراهم میکند، که در آن p یک فیلتر دلخواه است. این ویژگی انتسابها در jq را در مقایسه با سایر زبانها به طور منحصر به فردی قدرتمند میسازد. برای نمونه، برنامهای از راهنمای jq که ذهن مرا شگفتزده کرد، دستور زیر بود:
-
(.posts[] | select(.author == "stedolan") | .comments) += ["terrible."]
این فیلتر روی تمام پستها پیمایش میکند، پستهایی را که نویسنده آنها “stedolan” است انتخاب میکند، کامنتهای آنها را میگیرد، و یک نظر نه چندان خوشایند به آنها اضافه میکند. (این بازتابدهنده نظر من درباره استیون دولان نیست — به نظر من او در خلق jq کار فوقالعادهای انجام داده است.)
دستور jaq و برنامه jq دو رویکرد متفاوت را برای اجرای انتسابها دنبال میکنند:
- •
- مبتنی بر مسیر (Path-based): در jq، انتساب p |= f مسیرها را به تمام مقادیری که با p مطابقت دارند میسازد و فیلتر f را روی این مقادیر اعمال میکند.
- •
- بدون مسیر (Pathless): در jaq، یک انتساب p |= f به یک فیلتر متفاوت تبدیل میشود که هیچ مسیری نمیسازد.
برای نمونه، بهروزرسانی زیر را در نظر بگیرید: [1, 2, 3] | .[] |= .+1 --> [2, 3, 4]: هنگامی که jq این دستور را اجرا میکند، عبارت [1, 2, 3] | path(.[]) --> [0] [1] [2] را محاسبه کرده و .+1 را روی هر مقدار در این مسیرها اعمال میکند. از سوی دیگر، jaq این بهروزرسانی را به صورت زیر تبدیل میکند: [1, 2, 3] | [.[] | .+1] --> [2, 3, 4] — این انتساب شامل ساخت هیچ مسیری نمیشود.
خوشبختانه، همانند مثال فوق، در بیشتر موارد نتیجه هر دو رویکرد یکسان است. بخشهای زیر این دو رویکرد را با جزئیات بیشتر توضیح میدهند، و بیان میکنند که چگونه میتوان بهروزرسانیهایی نوشت که در هر دو مفسر jq و jaq رفتار یکسانی داشته باشند.
مبتنی بر مسیر (Path-based)
مدل بهروزرسانی مبتنی بر مسیر که توسط jq استفاده میشود، p |= u را با ابتدا جمعآوری مسیرهای متناظر با p، و سپس بهروزرسانی ورودی در این مسیرها با u اجرا میکند. ما میتوانیم این رفتار را با getpath(path(p)) |= u تقریبی شبیهسازی کنیم — رفتار واقعی بهروزرسانی در jq بسیار پیچیدهتر است، تنها به صورت مختصر مستند شده است، و بین نسخههای جزئی دچار تغییرات ناسازگار با گذشته شده است.
فیلترهای reduce / foreach هر دو در jaq بر اساس فیلترهای سادهتر f تعریف شدهاند که path(f) میتواند آنها را ارزیابی کند. بنابراین، در jaq، میتوانید از reduce / foreach درون path(f) و همچنین در سمت چپ بهروزرسانیها استفاده کنید. برای نمونه:
- •
- [{a: 1}] | path(reduce (0, "a") as $i (.; .[$i])) --> [0, "a"]
- •
- [{a: 1}] | path(foreach (0, "a") as $i (.; .[$i])) --> [0] [0, "a"]
به طور مشابه، میتوانید از مسیرهای ترکیبی استفاده کنید:
- •
- [1, 2, 3] | path(.[]?) --> [0] [1] [2]
- •
- "" | path(.[]?) --> (بدون خروجی)
- •
- {a: [1, 2]} | path(.a[]) --> ["a", 0] ["a", 1]
تمام فیلترهای دیگر تعریفشده در زبان هسته منجر به خطا میشوند، زیرا آنها به جای برگرداندن ارجاع به ورودیهایشان، مقادیر جدیدی میسازند. برای نمونه:
- •
- try path(0 ) catch "fail" --> "fail"
- •
- try path(1 + 2) catch "fail" --> "fail"
- •
- try path(1 = 2) catch "fail" --> "fail"
سازگاری
از سوی دیگر، jq نسبت به مسیرهای مقادیر میانی آسانگیرتر است. برای نمونه، دستور try path([] | empty) catch "fail" --> "fail"، زیرا jaq عبارت path([] | empty) را به path([]) as $p | ... کاهش میدهد، که شکست میخورد زیرا path([]) تعریف نشده است. در مقابل، jq برای این فیلتر هیچ خروجیای نمیدهد. جالب اینجاست که path([] | .[]) در هر دو مفسر jaq و jq خطا میدهد، بنابراین مشخص نیست چرا مثال با empty خطا نمیدهد.
در jaq، عبارت [] | path(.[0: ]) --> [{"start": 0}] و [] | path(.[ :0]) --> [{"end" : 0}] است، در حالی که jq به ترتیب [{start: 0 , end: null}] و [{start: null, end: 0 }] را برمیگرداند. این بدان معنی است که jq همواره ایندکسهای ناموجود start/end را به عنوان null اضافه میکند، در حالی که jaq آنها را حذف میکند.
بدون مسیر (Pathless)
مدل بهروزرسانی بدون مسیر که توسط jaq استفاده میشود، بهروزرسانیهای p |= u را بسته به p به عبارات سادهتر تقلیل میدهد. این روش در رایجترین موارد نتایج مشابهی با بهروزرسانیهای مبتنی بر مسیر تولید میکند، در حالی که مزایای زیر را دارد:
- •
- نیازی به ساخت مسیرها ندارد، که منجر به کارایی و عملکرد بالاتر میشود.
- •
- در صورت امکان، چندین خروجی توسط u را در نظر میگیرد، در حالی که بهروزرسانیهای مبتنی بر مسیر حداکثر یک خروجی را لحاظ میکنند. برای نمونه: 0 | (., .) |= (., .+1) --> 0 1 1 2 در jaq، در حالی که در jq تنها مقدار 0 را تولید میکند. با این حال، {a: 1} | .a |= (2, 3) --> {"a": 2} در هر دو jaq و jq است، زیرا یک شیء تنها میتواند یک مقدار منفرد را با هر کلید معین مرتبط سازد، بنابراین ما نمیتوانیم در اینجا از چند خروجی به شکل معناداری استفاده کنیم.
- •
- از مشکلات باطل شدن تکرارکننده (iterator invalidation) که بهروزرسانیهای مبتنی بر مسیر مستعد آن هستند، جلوگیری میکند.
با این حال، بهروزرسانیهای بدون مسیر از چند فیلتر در سمت چپ بهروزرسانیها که مدلهای مبتنی بر مسیر پشتیبانی میکنند، پشتیبانی نمینمایند؛ مانند:
- •
- try f catch g
- •
- first(f)، last(f)
- •
- limit($n; f)
- •
- skip($n; f)
برای نمونه، فیلترهای زیر همگی در jaq منجر به خطا میشوند:
- •
- [1, 2, 3] | try .[] -= 1 در jq خروجی [0, 1, 2] میدهد.
- •
- [1, 2, 3] | first( .[]) -= 1 در jq خروجی [0, 2, 3] میدهد.
- •
- [1, 2, 3] | limit(2; .[]) -= 1 در jq خروجی [0, 1, 3] میدهد.
- •
- [1, 2, 3] | skip (1; .[]) -= 1 در jq خروجی [1, 1, 2] میدهد.
- •
- [1, 2, 3] | last ( .[]) -= 1 در jq خطا میدهد.
در چنین مواردی، میتوانید با نوشتن getpath(path(p)) |= u به جای p |= u در jaq به بهروزرسانیهای مبتنی بر مسیر بازگردید. برای نمونه، فیلترهای زیر خروجیهای یکسانی در jaq و jq تولید میکنند:
- •
- [1, 2, 3] | getpath(path(try .[] )) -= 1 --> [0, 1, 2]
- •
- [1, 2, 3] | getpath(path(first( .[]))) -= 1 --> [0, 2, 3]
- •
- [1, 2, 3] | getpath(path(limit(2; .[]))) -= 1 --> [0, 1, 3]
- •
- [1, 2, 3] | getpath(path(skip (1; .[]))) -= 1 --> [1, 1, 2]
- •
- [1, 2, 3] | getpath(path(last ( .[]))) -= 1 --> [1, 2, 2] (این مثال در jq خطا میدهد، در حالی که در jaq کار میکند)
سازگاری
در jq، دستور [0, 1] | .[3] = 3 مقدار [0, 1, null, 3] را میدهد؛ یعنی jq در صورتی که فراتر از اندازه لیست بهروزرسانی کنیم، آن را با مقادیر null پر میکند. در مقابل، jaq در چنین حالتی با خطای خارج از محدوده (out-of-bounds) مواجه میشود.
برنامه jq از بهروزرسانی برشهای رشته پشتیبانی نمیکند، در حالی که jaq پشتیبانی مینماید.
در jq، null | .a = 1 مقدار {"a": 1} و null | .[0] = 1 مقدار [1] را میدهد؛ بدین معنا که jq هنگام بهروزرسانی null با یک ایندکس رشتهای یا عددی، با آن مانند آرایه یا شیء خالی رفتار میکند. از آنجا که jaq از کلیدهای شیء غیررشتهای پشتیبانی میکند، این کار دارای ابهام است، زیرا روشن نیست که آیا null | .[0] = 1 باید مقدار {0: 1} یا [1] را تولید کند. به همین دلیل، jaq در بهروزرسانیهای null با هر نوع ایندکسی خطایی صادر میکند.
الگوها (PATTERNS)
فیلتر f as $x | g خروجیهای f را به یک متغیر $x مقید میسازد. در جایگاه $x، میتوانیم از یک الگو (pattern) برای تخریب / ساختارشکنی (destructure) ورودی به چندین متغیر استفاده کنیم.
فیلتر زیر را در نظر بگیرید:
-
[ 1, {a: 2}] | .[0] as $x | .[1].a as $y | $x, $y --> 1 2
میتوانیم این را به شکل فشردهتر با استفاده از یک الگو چنین بنویسیم:
-
[ 1, {a: 2}] as [$x, {a: $y}] | $x, $y --> 1 2
در اینجا، [$x, {a: $y}] الگویی است که برای تطبیق با مقدار [1, {a: 2}] به کار میرود. این الگو 1 را به $x و 2 را به $y مقید میکند.
مشابه با ساخت شیء، {$x} معادل با {x: $x} برای الگوهای شیء نیز هست. برای نمونه، میتوانستیم مثال قبل را به شکل معادل چنین بنویسیم: [1, {a: 2}] as [$x, {$a}] | $x, $a --> 1 2
هنگامی که یک الگو در ورودی خود وجود نداشته باشد، متغیرهای متناظر آن به null مقید میشوند:
- •
- [1, {b: 2}] as [$x, {$a}] | $x, $a --> 1 null
- •
- [1 ] as [$x, $y] | $x, $y --> 1 null
- •
- [1, {a: 2}] as [$x, [$y]] | $x, $y --> 1 null
اگر نوعهای الگو و ورودی آن با هم مطابقت نداشته باشند، خطایی رخ میدهد:
- •
- try ([1] as {$a} | $a) catch "fail" --> "fail"
- •
- try ( 1 as [$x] | $x) catch "fail" --> "fail"
الگوها مجبور نیستند با تمام ورودی خود مطابقت داشته باشند:
- •
- [ 1, 2] as [$x] | $x --> 1
- •
- {a: 1, b: 2} as {$a} | $a --> 1
الگوها میتوانند به طور دلخواه تو در تو باشند:
- •
- {a: [1, {b: [2]}]} as {a: [$x, {b: [$y]}]} | $x, $y --> 1 2
- •
- [[[1]]] as [[[$x]]] | $x --> 1
- •
- {a: {b: {c: 1}}} as {a: {b: {c: $x}}} | $x --> 1
میتوانیم هر فیلتر (f) را به عنوان کلید شیء در یک الگو بنویسیم:
-
{a: 1, b: 2, c: 3, d: 4} as {("a", "b"): $x, ("c", "d"): $y} | [$x, $y] --> [1, 3] [1, 4] [2, 3] [2, 4]
همچنین میتوانیم از الگوها در reduce و foreach استفاده کنیم:
-
[{"a": 1, "b": 2}, {"a": 3, "b": 4}] | foreach .[] as {("a", "b"): $x} ([]; . + [$x]) --> [1] [1,2] [1,2,3] [1,2,3,4]
سازگاری
ماژولها (MODULES)
دستور jq امکان تقسیم برنامهها به چندین فایل را فراهم میکند که به آنها ماژول گفته میشود.
در ابتدای هر ماژول jq، یک هدر ماژول قرار دارد که از یک توالی (احتمالاً خالی) از دستورالعملهای فهرستشده در این بخش تشکیل شده است. هدر ماژول سپس توسط توالیای از تعاریف دنبال میشود. در نهایت، ماژول اصلی که از رابط خط فرمان فراخوانی میشود (از طریق --from-file یا به صورت درونخطی) باید حاوی یک فیلتر واحد در انتها باشد، که همان فیلتری است که اجرا میشود.
تمامی دستورالعملهای include/import فایلها را همانطور که در بخش مسیرهای جستجو توضیح داده شده است جستجو میکنند.
متادیتای ماژول (Module metadata)
دستورالعمل module meta; متادیتای ماژول جاری را روی خروجی meta تنظیم میکند، که در آن meta یک فیلتر است. این دستورالعمل ممکن است تنها در ابتدای هدر ماژول و تنها یک بار رخ دهد.
برای نمونه: module "My module"; 1 --> 1.
سازگاری
گنجاندن ماژول (Module inclusion)
دستورالعملهای include "mod"; و include "mod" meta; تمام فیلترهای تعریفشده در ماژول mod.jq را در ماژول جاری در دسترس قرار میدهند.
برای نمونه، اگر foo.jq در پوشه کاری جاری حاوی def bar: 1; باشد، دستور jaq -L . -n 'include "foo"; bar' مقدار 1 را بازمیگرداند.
وارد کردن ماژول (Module import)
دستورالعملهای import "mod" as name; و import "mod" as name meta; تمام تعاریف ماژول mod.jq را در ماژول جاری با پیشوند name:: در دسترس قرار میدهند.
برای نمونه:
-
$ echo 'def bar: 1;' > foo.jq && jaq -L . -n 'import "foo" as myfoo; myfoo::bar' && rm foo.jq 1
وارد کردن داده (Data import)
دستورالعملهای import "data" as $name; و import "data" as $name meta; تمام مقادیر JSON در data.json را درون یک آرایه بارگذاری کرده، آن را به متغیر $name مقید میکنند، و در ماژول جاری در دسترس قرار میدهند.
برای نمونه:
-
$ echo 1 2 3 > foo.json && jaq -L . -nc 'import "foo.json" as $myfoo; $myfoo' && rm foo.json [1,2,3]
مسیرهای جستجو (Search paths)
دستورالعمل include/import فایل مورد نظر خود را در دایرکتوریهای زیر و با ترتیب زیر جستجو میکند:
- •
- مسیرهای جستجوی سراسری مشخصشده از طریق --library-path. آنها به صورت نسبی نسبت به پوشه کاری جاری تفسیر میشوند.
- •
- مسیرهای جستجوی محلی مشخصشده از طریق متادیتا: هنگامی که یک دستورالعمل include/import دارای meta به شکل {..., search: ..., ...} باشد، مقدار موجود در کلید "search" مسیرهای جستجوی محلی را برای آن دستورالعمل به صورت زیر تعیین میکند:
- •
- اگر مقدار یک رشته باشد: فقط همان رشته.
- •
- اگر مقدار یک آرایه باشد: تمام رشتههای موجود در آرایه.
- •
- در غیر این صورت: هیچچیز.
اگر ماژول حاوی دستورالعمل include/import یک فایل باشد، این مسیرها به صورت نسبی نسبت به دایرکتوری والد آن ماژول تفسیر میشوند. در غیر این صورت، این مسیرها به صورت نسبی نسبت به پوشه کاری جاری تفسیر میشوند. (این حالت زمانی است که ماژول حاوی دستورالعمل include/import به صورت درونخطی در خط فرمان داده شده باشد.)
هر مسیر جستجوی سراسری و محلی به صورت زیر جایگزین میشود:
- •
- اگر با ~ آغاز شود، ~ با دایرکتوری خانگی کاربر که توسط متغیر محیطی HOME در لینوکس و USERPROFILE در ویندوز مشخص میشود جایگزین میگردد.
- •
- اگر با $ORIGIN آغاز شود، $ORIGIN با پوشهای که فایل اجرایی jaq در آن قرار دارد جایگزین میشود.
برای نمونه: jaq -L ~/foo -L bar 'include "decode" {search: ["baz", "$ORIGIN/quux"]}; 1' فایل decode.jq را در مسیرهای زیر و با ترتیب دادهشده جستجو میکند:
- •
- ~/foo/decode.jq (که در آن ~ با دایرکتوری خانگی کاربر جایگزین میشود)
- •
- ./bar/decode.jq
- •
- ./baz/decode.jq
- •
- $ORIGIN/quux/decode.jq (که در آن $ORIGIN با دایرکتوری والد فایل اجرایی jaq جایگزین میشود)
نخستین مسیری که با یک فایل موجود مطابقت داشته باشد برگزیده میشود.
حال فرض کنید که decode.jq حاوی دستورالعمل import "binary" as $binary {search: "."} باشد. این دستور binary.json را در مسیرهای زیر جستجو میکند:
- •
- ~/foo/binary.json
- •
- ./bar/binary.json (نسبت به پوشه کاری جاری)
- •
- ./binary.json (نسبت به پوشه والد decode.jq)
سازگاری
انگیزه رفتار jaq امکانپذیر ساختن دستورالعملهایی مانند import "binary.cbor" as $binary در آینده است. در اینجا، اضافه کردن بیقید و شرط پسوند .json نتیجه عکس خواهد داشت.
توضیحات / کامنتها (COMMENTS)
یک توضیح (comment) با # آغاز میشود و با نخستین خط جدیدی که قبل از آن تعداد فرد بکاسلش (\) نیامده باشد پایان مییابد. برای نمونه:
-
[ 1, # comment 2, # comment \\ 3, # comment \ comment 4, # comment \\\ comment \ comment 5 ]
این معادل با [1, 2, 3, 4, 5] است.
قالبها (FORMATS)
دستور jaq از خواندن و نوشتن چندین قالب داده پشتیبانی میکند. این بخش به شرح این قالبهای داده میپردازد.
شما میتوانید دادهها را در این قالبها با استفاده از یکی از دو روش زیر بارگذاری کرده و بنویسید:
- •
- گزینههای خط فرمان --from / --to، یا
- •
- فیلترهای سریالسازی و واسریالسازی مانند fromjson و toyaml
گزینههای خط فرمان --from و --to همواره عملکرد و کارایی بهتر یا برابری نسبت به فیلترهای متناظر ارائه میدهند.
JSON
JSON (نشانهگذاری شیء جاوااسکریپت) در RFC 8259 مشخص شده است.
دستور jaq میتواند تمام مقادیر معتبر JSON را بخواند؛ با این حال، همانند jq، مقادیر خاصی را که JSON نامعتبر هستند نیز میپذیرد. این مجموعه از مقادیر در بخش XJON مستند شده است.
XJON
قالب داده بومی jaq یک ابرمجموعه از JSON است که XJON نامیده میشود (eXtended JavaScript Object Notation، تلفظ مشابه “اکشن”).
قالب XJON استاندارد JSON را با ساختارهای زیر گسترش میدهد:
- •
- کامنتهای خطی: # ... \n به عنوان کامنت تفسیر میشود
- •
- اعداد ممیز شناور خاص: NaN، Infinity، +Infinity، -Infinity
- •
- اعدادی که با + یا چند صفر شروع میشوند: ابتدای یک عدد در XJON با عبارت منظم [+-]?\d+ و در JSON با -?(0|[1-9]\d*) مطابقت دارد. بدین معنا که در XJON، +7.1، 007، -042 همگی اعدادی معتبر هستند، در حالی که در JSON نامعتبرند.
- •
- رشتههای UTF-8 با واحدهای کد نامعتبر: استاندارد JSON تا حدی مبهم است که آیا رشتهها ممکن است حاوی واحدهای کد نامعتبر UTF-8 باشند یا خیر. قالب XJON صراحتاً واحدهای کد نامعتبر را در رشتههای UTF-8 مجاز میداند، برای نمونه خروجی printf '"\xFF"'. این امر سازگاری با ابزارهایی را که چنین رشتههایی تولید میکنند (مانند نام فایلها) افزایش میدهد. افزون بر این، امکان بارگذاری رشتهها در زمان ثابت را از طریق --rawfile فراهم میکند، جایی که jq به دلیل اعتبارسنجی UTF-8 زمان خطی مصرف میکند.
- •
- رشتههای بایتی: یک رشته بایتی از طریق b"..." ساخته میشود، که در آن ... توالیای از موارد زیر است:
- •
- بایتهایی در محدوده 0x20 تا (شامل) 0xFF، به استثنای نویسههای اسکی '"' و '\'
- •
- یک توالی گریز که با یک بکاسلش ('\') شروع شده و پس از آن b، f، n، r، t، '"'، '\'، یا xHH میآید که در آن HH یک عدد هگزادسیمال است. برای نمونه: b"Here comes \xFF, dadadada\nHere comes \xFF\nAnd I say: \"It's alright\"\x00". رشتههای بایتی با این ساختار در زبانهای دیگر نیز یافت میشوند؛ مانند راست و پایتون (با پیشوند b) و جاوااسکریپت و C (بدون پیشوند b).
- •
- اشیاء با کلیدهای غیررشتهای: در حالی که JSON کلیدهای اشیاء را به رشته محدود میکند، قالب XJON مقادیر دلخواه را به عنوان کلید شیء میپذیرد. برای نمونه: {null: 0, true: 1, 2: 3, "str": 4, ["arr"]: 5, {}: 6}
هدف از طراحی XJON پشتیبانی از مجموعهای از مقادیر موجود در YAML و CBOR بود؛ یعنی رشتههای بایتی و اشیاء دارای کلیدهای غیررشتهای، ضمن حفظ خوانایی انسانی قالب و سادگی و سرعت بالای تجزیه، همانند JSON.
قالب XJON میتواند هر مقدار jaq را بدون افت دقت کدگذاری کند؛ به ویژه اینکه رمزگشایی یک مقدار کدگذاریشده با XJON معادل با مقدار اولیه است. برای نمونه:
- •
- nan | . | isnan --> true در jaq و jq
- •
- nan | tojson | fromjson | isnan --> true در jaq، اما false در jq
این بدان معناست که tojson | fromjson در jaq معادل با . است، در حالی که در jq معادل \f[I]نیست\f[R]، به ویژه به دلیل مقادیر NaN و Infinity.
در حال حاضر هر جا که jaq مقدار JSON میپذیرد، XJON را نیز میپذیرد. این بدان معنی است که jaq مقادیر زیر را میپذیرد، حتی اگر JSON معتبر نباشند:
-
$ echo 'NaN b"Bytes" {1: 2} # Over and out' | jaq --from json -c NaN b"Bytes" {1:2}
YAML
YAML (YAML Ain’t Markup Language™) «یک زبان سریالسازی داده انسانپسند برای تمامی زبانهای برنامهنویسی» است. این قالب همچنین یک ابرمجموعه از JSON و XJON محسوب میشود. بدین معنا که هر مقدار jaq میتواند به عنوان یک مقدار YAML کدگذاری شود.
دستور jaq از خواندن YAML با انکرها / لنگرها (&foo) و نامهای مستعار (*foo) پشتیبانی میکند. این موارد امکان ایجاد ساختارهای داده به اشتراک گذاشته شده را فراهم میسازند. با این حال، jaq از دادههای خودارجاع پشتیبانی نمیکند؛ یعنی دادههایی که حاوی یک نام مستعار اشارهکننده به خودشان باشند. برای نمونه:
- •
- "[&a 1, &b 2, *a, *b]" | fromyaml --> [1, 2, 1, 2]
- •
- "[&b [&a [], *a], *b]" | fromyaml --> [[[], []], [[], []]]
- •
- "&a[*a]" | try fromyaml catch -1 --> -1
دستور jaq تگها را برای مقادیر اسکالر YAML مانند null، بولیها، اعداد و رشتهها اعتبارسنجی میکند:
- •
- "!!bool true" | fromyaml --> true
- •
- "!!int true" | try fromyaml catch "fail" --> "fail"
از سوی دیگر، jaq تگهای مربوط به آرایهها و اشیاء را نادیده میگیرد:
- •
- "!!foo []" | fromyaml --> []
- •
- "!!bar {}" | fromyaml --> {}
دستور jaq خروجی YAML بسیار نزدیکی به JSON/XJON تولید میکند. خروجی فشرده YAML در jaq عمدتاً از این نظر با XJON تفاوت دارد که رشتههای بایتی را به صورت رشته Base64 با تگ !!binary و مقادیر ممیز شناور خاص را به صورت .inf، -.inf و .nan مینویسد:
-
[infinite, -infinite, nan, ("a" | tobytes), {"a": 1}] | tojson, toyaml --> "[Infinity,-Infinity,NaN,b\"a\",{\"a\":1}]" "[.inf, -.inf, .nan, !!binary YQ==, {a: 1}]"
دستور jaq توالیهای نامعتبر UTF-8 را در رشتههای متنی هنگام نوشتن YAML حفظ میکند. با این حال، هنگام تلاش برای تجزیه YAML حاوی توالیهای نامعتبر UTF-8 خطا صادر میکند.
هنگام استفاده از --to yaml، دستور jaq علامت --- را قبل از هر مقدار خروجی و علامت ... را بعد از هر مقدار خروجی مینویسد. این کار برای مشخص کردن شروع و پایان اسناد YAML انجام میشود. برای نمونه:
-
$ jaq -n '[1, {a: 2, b: [3]}]' --to yaml --- - 1 - a: 2 b: - 3 ... $ echo 1 2 | jaq --to yaml --- 1 ... --- 2 ...
از گزینه -j برای حذف نشانگرهای شروع/پایان سند YAML استفاده کنید. این گزینه میتواند هنگام ادغام چند آرایه مفید باشد:
-
$ echo '[1, 2] [3, 4]' | jaq --to yaml -j - 1 - 2 - 3 - 4
هر دو گزینه --from yaml و فیلتر fromyaml کل ورودی را پیش از تجزیه در حافظه بارگذاری میکنند.
CBOR
CBOR (Concise Binary Object Representation) یک قالب باینری است که در RFC 8949 مشخص شده است.
مقادیر CBOR یک ابرمجموعه از مقادیر jaq هستند. این بدان معناست که مقادیر خاصی در CBOR وجود دارند که هیچ مقدار معادلی در jaq برای آنها وجود ندارد؛ برای نمونه:
دستور jaq هنگام تلاش برای رمزگشایی چنین مقادیر CBOR با شکست مواجه میشود.
هر مقدار jaq میتواند بدون افت دقت به عنوان مقدار CBOR کدگذاری شود، به جز رشتههای متنی حاوی واحدهای کد نامعتبر UTF-8. توالیهای نامعتبر UTF-8 با U+FFFD که شبیه این است: “” جایگزین میشوند.
دستور jaq توالیهای مقادیر CBOR را با الحاق بدون هیچ جداکنندهای مینویسد. این بدان معناست که --to cbor معادل با --to cbor --join-output است. دستور jaq همچنین میتواند توالیهای مقادیر متصل CBOR را بخواند.
TOML
TOML یک قالب فایل پیکربندی است. میتوانیم فایلهای TOML را مانند فایلهای JSON بخوانیم و جستجو کنیم:
-
$ jaq '.profile.release["codegen-units"]' Cargo.toml 1
افزون بر این، میتوانیم فایلهای TOML نیز بنویسیم:
-
$ echo 'a = {b = 1, c = {d = nan, e = [], f = [{g = inf}, {h = -inf}]}}' | jaq --from toml --to toml [a] b = 1 [a.c] d = nan e = [] [[a.c.f]] g = inf [[a.c.f]] h = -inf
در مقایسه با مقادیر jaq، قالب TOML مقادیر تاریخ-زمان دارد، اما TOML هیچیک از مقادیر null، رشتههای بایتی، یا کلیدهای غیررشتهای شیء را ندارد. علاوه بر این، ریشه مقدار TOML باید یک شیء باشد. این بدان معناست که نوشتن TOML ممکن است به روشهای گوناگونی با شکست مواجه شود:
- •
- 0 | try totoml catch -1 --> -1 (ریشه غیرشیء)
- •
- {a: null } | try totoml catch -1 --> -1 (مقدار null)
- •
- {a: 0 | tobytes} | try totoml catch -1 --> -1 (رشته بایتی)
- •
- {(1): 2 } | try totoml catch -1 --> -1 (کلید شیء غیررشتهای)
دستور jaq میتواند مقادیر TOML 1.1.0 را بخواند، به استثنای مقادیر تاریخ-زمان. دستور jaq به طور کلی باید بتواند مقادیر TOML را که خودش نوشته است دوباره بخواند، به استثنای اعداد صحیح بزرگ (big integers). مثال زیر یک شیء {a: 2^1024} حاوی یک عدد صحیح بزرگ میسازد، آن را با موفقیت به عنوان TOML مینویسد، و سپس هنگام خواندن مجدد آن شکست میخورد:
{a: nth(1024; 1 | recurse(.*2))} | totoml | try fromtoml catch -1 --> -1
هنگام نوشتن TOML، دستور jaq توالیهای نامعتبر UTF-8 را مشابه CBOR تبدیل میکند. این بدان معنی است که حتی اگر خواندن یک مقدار نوشتهشده TOML با موفقیت همراه باشد، خروجی آن ممکن است با ورودی یکسان نباشد:
{a: [-255] | implode} | (totoml | fromtoml) == . --> false
XML
دستور jaq دادههای منطبق بر استاندارد XML 1.0 را میخواند. با این حال، تنها دادههای XML کدگذاریشده به صورت UTF-8 را پردازش میکند.
دستور jaq میتواند فایلهای XHTML را بخواند، اما نمیتواند مستقیماً فایلهای HTML را بخواند. میتوانید از ابزارهایی مانند html2xhtml برای تبدیل HTML به XHTML استفاده کنید.
نگاشتهای میان XML و JSON به طور کلی باید بین «کاربرپسند بودن» (friendliness) و رفتوبرگشت کامل (round-tripping) مصالحه کنند؛ نگاه کنید به «Experiences with JSON and XML Transformations». در اینجا، «کاربرپسند بودن» بدین معنی است که JSON تولیدشده از XML ساختاری مسطح دارد، که مصرف آن را آسان میکند. اشتفان گوسنر بحث بسیار خوبی درباره نگاشتهای «کاربرپسند» گوناگون در مقاله «Converting Between XML and JSON» ارائه کرده است. پیام اصلی این است: نگاشتهای «کاربرپسند» اطلاعات را از دست میدهند. به همین دلیل، jaq از نگاشت «کاربرپسند» استفاده نمیکند، بلکه از نگاشتی بهره میبرد که اطلاعات XML را کاملاً حفظ میکند، و آن را برای رفتوبرگشت کامل (round-tripping) مناسب میسازد.
به عنوان نمونه، ورودی زیر را در نظر بگیرید:
-
<a href="https://www.w3.org">World Wide Web Consortium (<em>W3C</em>)</a>
میتوانیم نمایش داخلی آن را در jaq با دستور زیر ببینیم:
-
$ echo '<a href="https://www.w3.org">World Wide Web Consortium (<em>W3C</em>)</a>' \ | jaq --from xml . { "t": "a", "a": { "href": "https://www.w3.org" }, "c": [ "World Wide Web Consortium (", { "t": "em", "c": [ "W3C" ] }, ")" ] }
اشیاء TAC (TAC objects)
تگها توسط اشیاء TAC نمایش داده میشوند. یک شیء TAC میتواند فیلدهای زیر را داشته باشد:
- •
- t: نام تگ، مانند h1 برای <h1>...</h1>. این فیلد همواره باید در یک شیء TAC وجود داشته باشد.
- •
- a: مشخصهها و صفات تگ، مانند {"id": "foo", style: "color:blue;"}. اگر این فیلد وجود داشته باشد، باید شامل شیئی با مقادیر رشتهای باشد.
- •
- c: فرزندان تگ. اگر این فیلد وجود نداشته باشد، تگ به صورت خودبسته (self-closing، مانند <br/>) تفسیر میشود. هنگامی که یک شیء TAC تولیدشده توسط jaq (چه از طریق --from xml یا fromxml) دارای فیلد c باشد، همواره آرایهای از مقادیر XML را نگه میدارد. هنگام نوشتن مقادیر XML (چه از طریق --to xml یا toxml)، دستور jaq هر مقدار XML را در فیلد c میپذیرد.
یک پرسوجوی نمونه برای به دست آوردن تمام پیوندها در یک فایل XHTML:
-
.. | select(.t? == "a") | .a.href
ما همچنین میتوانیم XML ورودی را تغییر شکل داده و XML خروجی تولید کنیم. برای نمونه، جهت تبدیل تمام تگهای em به تگهای i:
-
(.. | select(.t? == "em") | .t) = "i"
برای تولید خروجی XML به جای خروجی JSON، از گزینه --to xml استفاده کنید:
-
$ echo '<a href="https://www.w3.org">World Wide Web Consortium (<em>W3C</em>)</a>' \ | jaq --from xml --to xml '(.. | select(.t? == "em") | .t) = "i"' <a href="https://www.w3.org">World Wide Web Consortium (<i>W3C</i>)</a>
در نهایت، میتوانیم تمام متن را از یک فایل XML استخراج کنیم (با صرفنظر از بلوکهای CDATA):
-
def xml_text: if isstring then . else .c[]? | xml_text end; [xml_text]
سایر مقادیر (Other values)
- •
- رشتهها اسکیپ یا غیراسکیپ نمیشوند؛ بدین معنی که Tom & Jerry در XML منبع تبدیل میشود به "Tom & Jerry" در JSON مقصد. فیلترهای @html/@htmld میتوانند برای اسکیپ کردن یا لغو اسکیپ دستی استفاده شوند.
- •
- یک کامنت مانند <!-- this comment --> تبدیل میشود به {"comment": " this comment "}.
- •
- یک بلوک CDATA مانند <![CDATA[Tom & Jerry]]> تبدیل میشود به {"cdata": "Tom & Jerry"}.
- •
- یک اعلان XML مانند <?xml version="1.0" encoding="UTF-8" standalone="yes"?> تبدیل میشود به {"xmldecl": {"version": "1.0", "encoding": "UTF-8", "standalone": "yes"}}. (توجه داشته باشید که مقادیر دادهشده در این اعلان، مانند کدگذاری، توسط تجزیهکننده XML در jaq نادیده گرفته میشوند.)
- •
- یک دستورالعمل پردازش (PI) مانند <?xml-stylesheet href="common.css"?> تبدیل میشود به {"pi": {"target": "xml-stylesheet", "content": "href=\"common.css\""}}.
برای کنار هم قرار دادن تمام این موارد، فایل XML زیر را در نظر بگیرید:
-
$ cat examples/test.xhtml <?xml version='1.0'?> <?xml-stylesheet href="common.css"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <body> <!-- CDATA blocks do not require escaping --> <![CDATA[Hello & goodbye!]]><br/> </body> </html>
این دستور در jaq خروجی زیر را میدهد:
-
$ jaq . examples/test.xhtml { "xmldecl": { "version": "1.0" } } { "pi": { "target": "xml-stylesheet", "content": "href=\"common.css\"" } } { "doctype": { "name": "html" } } { "t": "html", "a": { "xmlns": "http://www.w3.org/1999/xhtml" }, "c": [ "\n ", { "t": "body", "c": [ "\n ", { "comment": " CDATA blocks do not require escaping " }, "\n ", { "cdata": "Hello & goodbye!" }, { "t": "br" }, "\n " ] }, "\n" ] }
خروجی شامل مقادیر متعددی است که تنها از فاصلههای خالی تشکیل شدهاند، مانند "\n ". این مقادیر توسط jaq حفظ میشوند زیرا XML قالبی حساس به فاصلههای خالی است.
CSV / TSV
CSV (مقادیر جداشده با کاما) و TSV (مقادیر جداشده با تب) قالبهای داده جدولی هستند. یک جدول CSV/TSV از ردیفهایی از فیلدها تشکیل شده است. ردیفها با خطوط جدید از هم جدا میشوند. فیلدها در CSV با کاما و در TSV با نویسه تب از هم تفکیک میگردند.
دستور jaq هر ردیف را به عنوان آرایهای از مقادیر تفسیر میکند:
-
$ printf "a,b\n1,2\n3,4" | jaq -c --from csv ["a","b"] [1,2] [3,4] $ printf "a,b\n1,2\n3,4" | jaq -c --from csv --slurp [["a","b"],[1,2],[3,4]]
هنگام نوشتن جداول با استفاده از --to، دستور jaq خروجی خام فیلتر tocsv / totsv را چاپ میکند:
-
$ jaq -n '["a","b"], [1,2], [3,4]' --to csv "a","b" 1,2 3,4 $ jaq -n '["a","b"], [1,2], [3,4] | @csv' --raw-output "a","b" 1,2 3,4
سرستونها (Headers)
دستور jaq در حال حاضر پردازش ویژهای برای سرستونهای جدول ارائه نمیدهد، که در آن ردیف نخست جدول حاوی نام فیلدها است. با این حال، چند فیلتر در examples/table.jq وجود دارد که هدف آنها سادهسازی مدیریت جداول دارای سرستون است. اگر این فیلترها را مفید یافتید یا مشکلی در آنها دیدید، لطفاً بازخورد خود را ارسال کنید تا به من در تصمیمگیری برای ادغام آنها در کتابخانه استاندارد jaq کمک کنید. بیایید نحوه استفاده از آنها را ببینیم.
برای نمونه، بیایید به پرونده تحصیلی فریس بیولر نگاهی بیندازیم:
-
$ cat examples/ferris.csv ID,Name,Home phone,Days absent 38-106,"Bueller, Ferris",555-6452,9
خواندن این فایل کمی دشوار است، و ادوارد آر. رونی، ناظم مدرسه، فردی پرمشغله است. بیایید کمی به او کمک کنیم:
-
$ jaq -L examples 'include "table"; from_table' -s examples/ferris.csv { "ID": "38-106", "Name": "Bueller, Ferris", "Home phone": "555-6452", "Days absent": 9 }
بسیار بهتر شد. حالا ادوارد آر. رونی درباره فریس به مادر او چه میگوید؟
«تا اینجای ترم، اون نُه بار غیبت داشته.» — «نُه بار؟» — «نُه بار.» — «یادم نمیاد اون نُه بار مریض شده باشه.» — «احتمالاً به این خاطره که مریض نبوده؛ داشته مدرسه رو میپیچونده. چشماتون رو باز کنید خانم بیولر. سرتون کلاه گذاشته.» — «باورم نمیشه.» — «همینجا جلوم دارمش. اون نُه روز غیبت داشته.»
حالا نوبت درخشش فریس است:
-
$ jaq -L examples 'include "table"; from_table | .["Days absent"] -= 7' -s examples/ferris.csv { "ID": "38-106", "Name": "Bueller, Ferris", "Home phone": "555-6452", "Days absent": 2 }
«گریس! گریسسسسس!!!»
در ادامه، نگاهی به دروس فریس بیندازیم:
-
$ cat examples/ferris-courses.tsv Perds Days Course Teacher Rm Grade 1 Grade 2 Grade 3 Grade EX 01-04 MTWF Eng Comp Hollndr 221 B+ A 05-08 MWTF Calculus McMurry 309 B A- 09-10 TWTF Chemistry Gunner 260 A- A 11-13 All Lunch Caf 14-19 MTWF Gym Carlyle 127 B+ A- 23-25 TWTF Computr Sc Cohen 114 A- A 26-29 MWTF Utpian Scy Jardin 241 A A 30-32 MTWF Euro Hist Rice 334 B+ B+
بیایید ببینیم … فریس در کدام درسها به عنوان نمره اول +B گرفته است؟ کسی نظری دارد؟
-
$ jaq -L examples 'include "table"; with_table(map(select(.["Grade 1"] == "B+")))' \ -s examples/ferris-courses.tsv --to tsv Perds Days Course Teacher Rm Grade 1 Grade 2 Grade 3 Grade EX 01-04 MTWF Eng Comp Hollndr 221 B+ A 14-19 MTWF Gym Carlyle 127 B+ A- 30-32 MTWF Euro Hist Rice 334 B+ B+
بله، فریس … هنوز کمی کار روی پایگاهداده داری.
تا اینجا، ما تمام جداول را پیش از پردازش با استفاده از -s بارگذاری (slurp) کردیم. این کار میتواند به ویژه هنگام پردازش جداول بزرگ ناکارآمد باشد، زیرا کل جدول را درون حافظه میخواند. ما همچنین میتوانیم جداول را ردیفبهردیف بخوانیم: یک الگوی مفید در اینجا استفاده از inputs بدون گزینه معمول -n است. بدین ترتیب، ردیف اول (= سرستون) ورودی فیلتر میشود، و ردیفهای باقیمانده میتوانند با inputs بازیابی شوند. با در کنار هم قرار دادن اینها، میتوانیم مثال قبلی را بازنویسی کنیم:
-
$ jaq -L examples 'include "table"; ., . as $header | from_row(inputs) | select(.["Grade 1"] == "B+") | to_row($header)' \ examples/ferris-courses.tsv --to tsv Perds Days Course Teacher Rm Grade 1 Grade 2 Grade 3 Grade EX 01-04 MTWF Eng Comp Hollndr 221 B+ A 14-19 MTWF Gym Carlyle 127 B+ A- 30-32 MTWF Euro Hist Rice 334 B+ B+
این بخش ماجرای فریس بیولر را به پایان میرساند. اگر هنوز این فیلم را ندیدهاید، اکیداً پیشنهاد میکنم آن را تماشا کنید. و به یاد داشته باشید: «زندگی با سرعت زیادی میگذره. اگه گهگاهی نایستی و به اطرافت نگاه نکنی، ممکنه از دستش بدی.»
مثالها (EXAMPLES)
مثالهای زیر باید دیدی کلی از آنچه jaq در حال حاضر قادر به انجام آن است ارائه دهند. با جایگزین کردن jaq با jq باید دقیقاً همان خروجیها را دریافت کنید.
دسترسی به یک فیلد:
-
$ echo '{"a": 1, "b": 2}' | jaq '.a' 1
جمع مقادیر:
-
$ echo '{"a": 1, "b": 2}' | jaq 'add' 3
ساخت یک آرایه از یک شیء به دو روش و نشان دادن برابری آنها:
-
$ echo '{"a": 1, "b": 2}' | jaq '[.a, .b] == [.[]]' true
اعمال یک فیلتر روی تمام عناصر یک آرایه و فیلتر کردن نتایج:
-
$ echo '[0, 1, 2, 3]' | jaq -c 'map(.*2) | [.[] | select(. < 5)]' [0,2,4]
خواندن (slurp) مقادیر ورودی درون یک آرایه و به دست آوردن میانگین عناصر آن:
-
$ echo '1 2 3 4' | jaq -s 'add / length' 2.5
اعمال مکرر یک فیلتر روی خودش و خروجی دادن نتایج میانی:
-
$ echo '0' | jaq -c '[recurse(.+1; . < 3)]' [0,1,2]
تاکردن غیرحریصانه (lazy fold) روی ورودیها و خروجی دادن نتایج میانی:
-
$ seq 5 | jaq -n 'foreach inputs as $x (0; . + $x)' 1 3 6 10 15
معمای لوئیس (LEWIS’S PUZZLE)
معمای زیر در یک کارگاه آموزشی توسط فردی به نام آقای لوئیس با من مطرح شد، جایی که آن را همراه او در jq حل کردم. معما به شرح زیر است:
ما دنبالهای از رشتهها داریم:
-
X XYZX XYZXABXYZX
برای نمونه، چهارمین حرف از رشته دوم (همیشه با شروع شمارش از صفر) حرف ‘A’ است. حرف ۱۰۲۴۴ام از رشته سیام چیست؟
نخست، بیایید بفهمیم این دنباله چگونه ساخته میشود. برای به دست آوردن دنباله بعدی حروف، دنباله قبلی را برمیداریم، آن را با دو حرف بعدی الفبا الحاق میکنیم، و سپس دوباره با دنباله قبلی الحاق مینماییم.
اگر به جای حروف از اعداد استفاده کنیم، میتوانیم آن را به این صورت بنویسیم:
-
X(0 ) = 0 X(N+1) = X(N) (m+1) (m+2) X(N), where m is the largest element in X(N)
اکنون میتوانیم این رشتهها را به عنوان آرایههای JSON بنویسیم. آرایه اول [0] است، و میتوانیم هر آرایه بعدی را با فیلتر next بسازیم، که با recurse روی آن دنبالهای از تمام آرایهها را به دست میآوریم.
-
def next: . + [max + (1, 2)] + .; [0] | limit(3; recurse(next)) --> [0] [0,1,2,0] [0,1,2,0,3,4,0,1,2,0]
با این حال، این روش مقیاسپذیر نیست — رسیدن به آرایه سیام زمان بسیار زیادی خواهد برد، زیرا آرایهها به صورت نمایی رشد میکنند. میتوانید آن را امتحان کنید، اما مراقب باشید رم (RAM) سیستم تمام نشود. :) (توصیه میکنم هنگام انجام این آزمایش مصرف رم را زیر نظر داشته باشید؛ در غیر این صورت، ممکن است رایانه شما به دلیل اتمام حافظه به کلی متوقف شود. حدس بزنید از کجا میدانم؟)
برای حل این مشکل، میتوانیم از اشتراکگذاری دادهها در jq بهره ببریم. توجه داشته باشید که هر آرایه شامل بخشی در سمت چپ است که برابر است با بخشی در سمت راست؛ برای نمونه، [0, 1, 2, 0] در آرایه دوم. بنابراین میتوانیم نمایشی اندکی متفاوت از آرایه انتخاب کنیم که به ما امکان میدهد بخشهای برابر آرایه را به سادگی با قرار دادن آرایههای قبلی درون یک آرایه جدید، به \f[B]اشتراک\f[R] بگذاریم.
-
def next: [., .[2] + (1,2), .]; [0] | limit(3; recurse(next)) --> [0] [[0],1,2,[0]] [[[0],1,2,[0]],3,4,[[0],1,2,[0]]]
در تمامی آرایههای تولیدشده توسط next، عناصر اول و آخر اکنون به اشتراک گذاشته شدهاند، بدین معنی که فقط یک بار در حافظه ذخیره میشوند. این کار به ما امکان میدهد دادههای با اندازه نمایی را در حافظه خطی ذخیره کنیم، و بدین ترتیب معما را حل کنیم.
برای به دست آوردن بزرگترین عدد آرایه قبلی، از این واقعیت استفاده کردیم که عنصر دوم هر آرایه حاوی بزرگترین عدد موجود در آرایه است. برای نمونه: عنصر دوم آرایه اول 2 است، و عنصر دوم آرایه دوم 4 است. (برای آرایه صفرم، عنصر دوم null است، اما بیشینه عنصر آن آرایه 0 است. با این حال، null در عمل جمع دقیقاً مانند 0 تفسیر میشود، بنابراین این تفاوت اهمیتی ندارد.) بنابراین میتوانیم دو عدد بزرگتر بعدی را به زیبایی از طریق .[2] + (1, 2) به دست آوریم.
اکنون میتوانیم اعداد هر چنین آرایهای را با .. | numbers دریافت کنیم. برای نمونه، اعداد آرایه دوم عبارتند از:
-
[[[0],1,2,[0]],3,4,[[0],1,2,[0]]] | .. | numbers --> 0 1 2 0 3 4 0 1 2 0
با کنار هم قرار دادن تمام این موارد، راهحل ما حاصل میشود:
-
def next: [., .[2] + (1,2), .]; [0] | nth(30; recurse(next)) | nth(10244; .. | numbers) --> 2
این دستور اکنون تقریباً آنی اجرا میشود و پاسخ 2 را به ما میدهد. با بازگشت به معمای اصلی، از آنجا که X = 0، Y = 1 و Z = 2 است، پاسخ نهایی معما حرف Z است.
استخراج از HTML (HTML SCRAPING)
من میخواستم فهرست مثالها را از مشخصات CBOR استخراج کنم تا مجموعه آزمایشی برای رمزگذار/رمزگشای CBOR در jaq ایجاد کنم. برای این کار، بخش مربوطه را از کد منبع HTML کپی کرده و در فایل examples/cbor-examples.xhtml قرار دادم. بخشهای جالب آن به این صورت است:
-
<table> <tbody> <tr> <td class="text-left" rowspan="1" colspan="1">25</td> <td class="text-left" rowspan="1" colspan="1">0x1819</td> </tr> <tr> <td class="text-left" rowspan="1" colspan="1">100</td> <td class="text-left" rowspan="1" colspan="1">0x1864</td> </tr> </tbody> </table>
در اینجا، من به جفتهای 25 / 0x1819 و 100 / 0x1864 علاقهمند بودم؛ بدین معنی که عدد 25 در CBOR به صورت 0x1819 کدگذاری میشود.
در نهایت، من برنامهای در jq برای استخراج این دادهها طراحی کردم. این برنامه از مراحل زیر تشکیل شده است:
- •
- انتخاب تمام عناصر <tr> با .. | select(.t? == "tr").
- •
- دریافت فرزندان آن با .c.
- •
- پیمایش روی فرزندان با .[] و دریافت فرزندان آنها با .c?[].
(من در اینجا به جای .c از .c? استفاده میکنم چون XML به فاصلههای خالی حساس است؛ بنابراین در <tr> <td></td> </tr>، عنصر <tr> در واقع سه فرزند دارد: دو رشته فاصله " " و عنصر <td>. ایندکس کردن رشتهها با .c خطا میدهد، در حالی که ایندکس کردن آنها با .c? صرفاً هیچ چیزی برنمیگرداند و به ما امکان میدهد فاصلهها را نادیده بگیریم.)
میتوانیم اثرات آن را روی نسخهای سادهشده از HTML ببینیم:
-
"<table>" + "<tr> <td>25</td> <td>0x1819</td> </tr>" + "<tr> <td>100</td> <td>0x1864</td> </tr>" + "</table>" | fromxml | .. | select(.t? == "tr").c | [.[].c?[]] --> [ "25", "0x1819"] ["100", "0x1864"]
برای اجرای این دستور از طریق خط فرمان (CLI):
-
jaq '.. | select(.t? == "tr").c | [.[].c?[]]' examples/cbor-examples.xhtml
میتوانیم مجموعهای از آزمونها را به صورت زیر ایجاد کنیم:
-
jaq '.. | select(.t? == "tr").c | [.[].c?[]] | @json "jc(\(.[0]), \(.[1][2:]));"' \ examples/cbor-examples.xhtml -r
من دقیقاً از همین دستور برای ساخت پیشنویس آزمونهای تجزیه CBOR در jaq استفاده کردم.
نویسنده (AUTHOR)
Michael Färber