Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Liz Lam
Liz Lam

Posted on • Edited on

Use jq to format p4 changes output to JSON.

UPDATE: Just a few days after hitting publish on this blog, a few folks from the Perforce community shared a better way to output JSON data from p4 commands using the-ztag and-Mj flags:p4 -ztag -Mj changes -m3 | jq -s '.'

I recently ran acrossthis blog showing how to usejq to formatgit log output to JSON. Curious aboutjq and wanting to try a similar thing with Perforce, I was able to modify the example pretty easily.

This is whatp4 changes output looks like:

$ p4 changesChange 3 on 2023/07/20 by grepliz@test-local-01 'Add day1.pdf'Change 2 on 2023/07/20 by grepliz@test-local-01 'Add crazy.psd.'Change 1 on 2023/07/20 by grepliz@test-local-01 'Populated server with files '
Enter fullscreen modeExit fullscreen mode

Here is how you can format to JSON usingjq:

$ p4 changes | jq -R -s '[split("\n")[:-1] | map(split("\u0020")) | .[] | { "change": .[1], "author": .[5], "date": .[3], "message": .[6:length] | join(" ") }]'[  {    "change": "3",    "author": "grepliz@test-local-01"    "date": "2023/07/20",    "message": "'Add day1.pdf'"  },  {    "change": "2",    "author": "grepliz@test-local-01",    "date": "2023/07/20",    "message": "'Add crazy.psd.'"  },  {    "change": "1",    "author": "grepliz@test-local-01",    "date": "2023/07/20",    "message": "'Populated server with files '"  }]
Enter fullscreen modeExit fullscreen mode

I think the original blog I referred to gives a pretty good explanation of what is going on, but I find it helpful to write out what is going on for future reference (and for my future self!).

The input:p4 changes

I know the output ofp4 changes is being piped into thejq program and it is helpful that the output is delimited by a space character and is one line per changelist. This output produces the columns needed for programs likejq andawk.

The flags:jq -R -s

Thejq flags being used are nicelydocumented here.

--slurp/-s:
Instead of running the filter for each JSON object in the input, read the entire input stream into a large array and run the filter just once.

--raw-input/-R:
Don't parse the input as JSON. Instead, each line of text is passed to the filter as a string. If combined with --slurp, then the entire input is passed to the filter as a single long string.

Thejq filter

'[split("\n")[:-1] | map(split("\u0020")) | .[] | { "change": .[1], "author": .[5], "date": .[3], "message": .[6:length] | join(" ") }]'

This is where all the magic happens. There are basically 4 sections to this filter.

  • split("\n")[:-1]

This says to split on newlines. If we were to stop here, we would get an array of the changelists.
Example:

p4 changes | jq -R -s 'split("\n")[:-1]'[  "Change 3 on 2023/07/20 by grepliz@test-local-01 'Add day1.pdf'",  "Change 2 on 2023/07/20 by grepliz@test-local-01 'Add crazy.psd.'",  "Change 1 on 2023/07/20 by grepliz@test-local-01 'Populated server with files '"]
Enter fullscreen modeExit fullscreen mode
  • map(split("\u0020"))

This tells the filter to map and split on the space character.
When combined with #1 above it would look like this:

p4 changes | jq -R -s 'split("\n")[:-1] | map(split("\u0020"))'[  [    "Change",    "3",    "on",    "2023/07/20",    "by",    "grepliz@test-local-01",    "'Add",    "day1.pdf'"  ],  [    "Change",    "2",    "on",    "2023/07/20",    "by",    "grepliz@test-local-01",    "'Add",    "crazy.psd.'"  ],  [    "Change",    "1",    "on",    "2023/07/20",    "by",    "grepliz@test-local-01",    "'Populated",    "server",    "with",    "files",    "'"  ]]
Enter fullscreen modeExit fullscreen mode
  • .[]

This creates arrays of the values such that we can iterate over it. The following is from the documentation:

Array/Object Value Iterator: .[]
If you use the .[index] syntax, but omit the index entirely, it will return all of the elements of an array. Running .[] with the input [1,2,3] will produce the numbers as three separate results, rather than as a single array.

You can also use this on an object, and it will return all the values of the object.

p4 changes | jq -R -s 'split("\n")[:-1] | map(split("\u0020")) | .[]'[  "Change",  "3",  "on",  "2023/07/20",  "by",  "grepliz@test-local-01",  "'Add",  "day1.pdf'"][  "Change",  "2",  "on",  "2023/07/20",  "by",  "grepliz@test-local-01",  "'Add",  "crazy.psd.'"][  "Change",  "1",  "on",  "2023/07/20",  "by",  "grepliz@test-local-01",  "'Populated",  "server",  "with",  "files",  "'"]
Enter fullscreen modeExit fullscreen mode
  • { "change": .[1], "author": .[5], "date": .[3], "message": .[6:length] | join(" ") }

Finally, this section gives us the desired JSON shape of each changelist and by wrapping the whole thing (the whole jq filter) with[], we get the final array.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I love open source, coffee and tinkering.
  • Location
    Oakland
  • Work
    Software Engineer
  • Joined

More fromLiz Lam

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp