Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 months ago.
Improve this question
All,
This is my first time submitting a stack overflow question, so thanks in advance for taking the time to read/consider my question. I'm currently using the 'utmpdump' utility to dump linux authentication log results each hour from a bash script, which is done using the syntax shown below:
dateLastHour=$(date +"%a %b %d %H:" -d '1 hour ago')
dateNow=$(date +"%a %b %d %H:")
utmpdump /var/log/wtmp* | awk "/$dateLastHour/,/$dateNow/"
What I'm now trying to accomplish and the subject of this question is how can I take these results and delimited them by new line for each authentication log, before converting each authentication event into it's own JSON file to be exported to an external syslog collector for additional analysis and long term storage?
As an example, here's some of the test results I've been using:
[7] [08579] [ts/0] [egecko] [pts/0 ] [10.0.2.6 ] [1.1.1.1 ] [Fri Nov 04 23:40:29 2022 EDT]
[8] [08579] [ ] [ ] [pts/0 ] [ ] [0.0.0.0 ] [Fri Nov 04 23:55:16 2022 EDT]
[2] [00000] [~~ ] [reboot ] [~ ] [3.10.0-1160.80.1.el7.x86_64] [0.0.0.0 ] [Sat Dec 03 12:28:05 2022 EST]
[5] [00811] [tty1] [ ] [tty1 ] [ ] [0.0.0.0 ] [Sat Dec 03 12:28:12 2022 EST]
[6] [00811] [tty1] [LOGIN ] [tty1 ] [ ] [0.0.0.0 ] [Sat Dec 03 12:28:12 2022 EST]
[1] [00051] [~~ ] [runlevel] [~ ] [3.10.0-1160.80.1.el7.x86_64] [0.0.0.0 ] [Sat Dec 03 12:28:58 2022 EST]
[7] [02118] [ts/0] [egecko] [pts/0 ] [1.1.1.1 ] [1.1.1.1 ] [Sat Dec 03 12:51:22 2022 EST]
Any assistance or pointers here is greatly appreciated!
I've been using the following SED commands to trim out unnessecary whitespace, and I know that what I probably should do is using IDF to split the results string into new lines before using brackets as the delimeter:
utmpResults=$(echo "$utmpResults" | sed 's/ */ /g')
IFS="\n" read -a array <<< "$utmpResults"
echo $array
But when I echo $array it only returns the first line...?
With the help of jq (sed for json), it's an easy task:
#!/bin/bash
jq -R -c '
select(length > 0) | # remove empty lines
[match("\\[(.*?)\\]"; "g").captures[].string # find content within square brackets
| sub("^\\s+";"") | sub("\\s+$";"")] # trim content
| { # convert to json object
"type" : .[0],
"pid" : .[1],
"terminal_name_suffix" : .[2],
"user" : .[3],
"tty" : .[4],
"remote_hostname" : .[5],
"remote_host" : .[6],
"datetime" : .[7],
"timestamp" : (.[7] | strptime("%a %b %d %T %Y %Z") | mktime)
}' input.txt
Output
{"type":"7","pid":"08579","terminal_name_suffix":"ts/0","user":"egecko","tty":"pts/0","remote_hostname":"10.0.2.6","remote_host":"1.1.1.1","datetime":"Fri Nov 04 23:40:29 2022 EDT","timestamp":1667605229}
{"type":"8","pid":"08579","terminal_name_suffix":"","user":"","tty":"pts/0","remote_hostname":"","remote_host":"0.0.0.0","datetime":"Fri Nov 04 23:55:16 2022 EDT","timestamp":1667606116}
{"type":"2","pid":"00000","terminal_name_suffix":"~~","user":"reboot","tty":"~","remote_hostname":"3.10.0-1160.80.1.el7.x86_64","remote_host":"0.0.0.0","datetime":"Sat Dec 03 12:28:05 2022 EST","timestamp":1670070485}
{"type":"5","pid":"00811","terminal_name_suffix":"tty1","user":"","tty":"tty1","remote_hostname":"","remote_host":"0.0.0.0","datetime":"Sat Dec 03 12:28:12 2022 EST","timestamp":1670070492}
{"type":"6","pid":"00811","terminal_name_suffix":"tty1","user":"LOGIN","tty":"tty1","remote_hostname":"","remote_host":"0.0.0.0","datetime":"Sat Dec 03 12:28:12 2022 EST","timestamp":1670070492}
{"type":"1","pid":"00051","terminal_name_suffix":"~~","user":"runlevel","tty":"~","remote_hostname":"3.10.0-1160.80.1.el7.x86_64","remote_host":"0.0.0.0","datetime":"Sat Dec 03 12:28:58 2022 EST","timestamp":1670070538}
{"type":"7","pid":"02118","terminal_name_suffix":"ts/0","user":"egecko","tty":"pts/0","remote_hostname":"1.1.1.1","remote_host":"1.1.1.1","datetime":"Sat Dec 03 12:51:22 2022 EST","timestamp":1670071882}
Without the option -c you can create formatted output.
To save each line in a file, you can do it like this in bash.
I have chosen the timestamp as the file name.
INPUT_AS_JSON_LINES=$(
jq -R -c '
select(length > 0) | # remove empty lines
[match("\\[(.*?)\\]"; "g").captures[].string # find content within square brackets
| sub("^\\s+";"") | sub("\\s+$";"")] # trim content
| { # convert to json object
"type" : .[0],
"pid" : .[1],
"terminal_name_suffix" : .[2],
"user" : .[3],
"tty" : .[4],
"remote_hostname" : .[5],
"remote_host" : .[6],
"datetime" : .[7],
"timestamp" : (.[7] | strptime("%a %b %d %T %Y %Z") | mktime)
}' input.txt
)
while read line
do
FILENAME="$(jq '.timestamp' <<< "$line").json"
CONTENT=$(jq <<< "$line") # format json
echo "writing file '$FILENAME'"
echo "$CONTENT" > "$FILENAME"
done <<< "$INPUT_AS_JSON_LINES"
Output
writing file '1667605229.json'
writing file '1667606116.json'
writing file '1670070485.json'
writing file '1670070492.json'
writing file '1670070492.json'
writing file '1670070538.json'
writing file '1670071882.json'
Related
Known
Consider the following command:
ls -l
-rwx------ 123 ladiesman217 root 549 Apr 01 01:01 one.txt
-rwx------ 123 ladiesman217 root 549 Apr 01 02:02 two.txt
-rwx------ 123 ladiesman217 root 549 Apr 01 03:03 three.txt
When piped to ConvertTo-Json, it creates an array like so:
ls -l | ConvertTo-Json
[
"-rwx------ 123 ladiesman217 root 549 Apr 01 01:01 one.txt",
"-rwx------ 123 ladiesman217 root 549 Apr 01 02:02 two.txt",
"-rwx------ 123 ladiesman217 root 549 Apr 01 03:03 three.txt"
]
Objective
Is there a quick way to make it into a 2d array something like:
[
[
"-rwx------",
"123",
"ladiesman217",
"root",
"549",
"Apr",
"01",
"01:01",
"one.txt"
],
[
"-rwx------",
"123",
"ladiesman217",
"root",
"549",
"Apr",
"01",
"02:02",
"two.txt"
],
[
"-rwx------",
"123",
"ladiesman217",
"root",
"549",
"Apr",
"01",
"03:03",
"three.txt"
]
]
In essence, it would be something like converting an object into an array.
How to convert the result of a command into a 2D array in PowerShell?
To make a 2D array exactly like you've shown, you can do this.
ls -l | Select-Object -Skip 1 | %{, -split $_ | ConvertTo-Json}
However to instead make usable objects with properties, I like this recipe.
Switch statement
Just enough regex
A pinch of string manipulation
Unary operator
PSCustomObject
$output = switch -Regex (ls -l){
'(.+?)\s(\w{3}\s.+?:\d{1,})\s(.+?)$' {
, -split $matches.1 | Foreach-Object {
[PSCustomObject]#{
Permission = $_[0]
Links = $_[1]
Owner = $_[2]
Group = $_[3]
Size = $_[4]
Modified = $matches.2
FileName = $matches.3
}
}
}
}
$output | Format-Table
Output
TL;DR
The switch statement is great for reading lines whether that's from a file, command output, etc. We only grab those lines that match the regex pattern.
Regex explanation
.+? says to match any amount of any character, but as few as possible.
( ) puts anything that matches the pattern in the parenthesis in a capture group. By default will be numbered groups, can name them if you like.
\s match a single space
\w{3} match 3 word characters
: match a literal colon
\d{1,} match one or more numerical digits
$ the end of the line. (the match right before this must be at the end of the line)
So we capture the first 5 columns in $matches.1, the time stamp in $matches.2, and the rest of the text (which should be file/folder name) in $matches.3
From there we split $matches.1 at the spaces by putting the split first. Normally when you split and send down the pipeline it will be one at a time.
-split "a b c" | %{"$_"}
By adding the comma to the front that turns it into an array and is sent down the pipeline as such.
, -split "a b c" | %{"$_"}
Then we just put each value in the correct spot. You can also turn into a JSON object.
With this you could convert the result of ls -l to a PS Object which then can be piped to ConvertTo-Json in case you need Json as result. Now I'm not very good with regex, this example will work only if the files / folders don't have white spaces. If someone can help with the correct regex to split it, that would be great:
$lsObject=ls -l|select -skip 1|%{
$tmp=$_ -split '\s{1,}'
[PSCustomObject]#{
Permission = $tmp[0]
Links = $tmp[1]
Owner = $tmp[2]
Group = $tmp[3]
Size = $tmp[4]
LastModified = '{0} {1} {2}' -f $tmp[5],$tmp[6],$tmp[7]
FileName = $tmp[8]
}
}
Here is how the object looks:
I'm trying to parse the below json array and get the values from it. But it is not working with spaces as values. I did find some solutions in stackoverflow but nothing seems to work.
JSON
{
"apps": [
{
"name": "Root Certification Authority - G2",
"expiryDate": "Monday, 09 October 2023 20:03:25",
"impactStatement": "Apps using this root certificate will have an actual impact",
"notifyBeforeInDays": 60
},
{
"name": "Bamboo",
"expiryDate": "Sunday, 20 November 2022 03:25:23",
"impactStatement": "CI/CD wont be working",
"sop": "https://somelink/Bamboo+SOPs",
"notifyBeforeInDays": 30
},
{
"name": "Vault - Client",
"expiryDate": "Monday, 09 October 2023 20:03:25",
"impactStatement": "All Mule applications for that particular environment will stop working",
"notifyBeforeInDays": 60
},
{
"name": "Consul",
"expiryDate": "Monday, 21 August 2023 14:43:56",
"impactStatement": "No Direct impact or never had any such scenario so far",
"notifyBeforeInDays": 30
},
{
"name": "bitbucket",
"expiryDate": "08 September 2021 13:16:06",
"impactStatement": "No Impact",
"notifyBeforeInDays": 15
}
]
}
And I'm using the below code to parse the json
appls=$(awk '{print $0}' ${work_dir}/scripts/applications.json | jq -c '. |select(.apps !=null) |.apps[]')
echo "*****"
echo $appls
echo "*****"
for row in ${appls}; do
echo $row
validateAndNotify $row
done
And when i print the above variable following output is printed, which is not a valid one.
*****
{"name":"bitbucket","expiryDate":"08 September 2021 13:16:06","impactStatement":"No Impact","notifyBeforeInDays":15} such scenario so far","notifyBeforeInDays":30}","notifyBeforeInDays":60}Bamboo+SOPs","notifyBeforeInDays":30}
*****
{"name":"Root
I want to parse the apps array and get each value inside that node.
You need to quote your variables in the shell to prevent from word splitting:
#!/bin/bash
work_dir="/foo/bar"
validateAndNotify() {
echo "${1}"
}
appls=$(jq -c '.apps[]?' "${work_dir}/scripts/applications.json")
echo "*****"
echo "${appls}"
echo "*****"
while read -r row ; do
validateAndNotify "${row}"
done <<< "${appls}"
PS: Shortened the jq command. Thanks jthill
(Title edited. The original title: "json content: print out parts of it as is, pipe parts of it to get human readable timestamps, output from the same command")
I have a json alike content in a file:
{
"newState": "runnable",
"setAt": 1587421159359
}
{
"newState": "running",
"setAt": 1587421282891
}
{
"newState": "debug_hold",
"setAt": 1587422014895
}
{
"newState": "terminating",
"setAt": 1587424788577
}
{
"newState": "failed",
"setAt": 1587424796544
}
I can extract the 'newState' by cat timestamps.json | jq -r '.newState':
runnable
running
debug_hold
terminating
failed
I can extract the epoch timestamps and format it into a human readable form by cat timestamps.json | jq -r '.setAt' | rev | cut -c 4- | rev | perl -pe 's/(\d+)/localtime($1)/e':
Mon Apr 20 18:19:19 2020
Mon Apr 20 18:21:22 2020
Mon Apr 20 18:33:34 2020
Mon Apr 20 19:19:48 2020
Mon Apr 20 19:19:56 2020
How can I combine the two outputs so the result becomes
runnable Mon Apr 20 18:19:19 2020
running Mon Apr 20 18:21:22 2020
debug_hold Mon Apr 20 18:33:34 2020
terminating Mon Apr 20 19:19:48 2020
failed Mon Apr 20 19:19:56 2020
I think I can do some bash for loop and array input but was wondering if jq has something that can pipe a portion of the content (e.g. epoch time in this case) out, process it, then feed the value back into the jq parse output.
You may be looking something like this.
cat timestamps.json | jq -r '[.newState, .setAt] | join(" ")'
With input being a collection of (unrelated) valid JSON strings you can read in {} chunks.
Set the input record separator ($/) to } and then the <> operator each time reads up to }
use warnings;
use strict;
use feature 'say';
use JSON qw(decode_json);
my $file = shift // die "Usage: $0 file\n"; #/
open my $fh, '<', $file or die "Can't open $file: $!";
local $/ = '}'; # presumably all this code is in some local scope
while (my $record = <$fh>) {
next if not $record =~ /\S/;
my $json = decode_json($record);
say $json->{newState}, ' ', scalar localtime $json->{setAt}/1000;
}
Comments
This relies on the shown format of the input, in particular that it has no nested objects. If there are nested {...} then slurp the whole file and extract JSON strings using Text::Balanced or equivalent (or, of course, use another approach)
I'd actually recommend to use Cpanel::JSON::XS
When global variables like $/ need be changed that is best done in the smallest scope needed and with local. Here it doesn't matter but I presume this to be a part of a larger program
There may be empty strings and, in particular, newlines left over when reading this way thus the check of whether the record contains any non-whitespace
The timestamps in your input are off by a factor of thousand from seconds-since-epoch, I guess because they carry milliseconds as well. I just divide by 1000 for simplicity
Note that the shown desired timestamps may become a problem if daylight saving time gets involved, and if that is the case you want to extract and include the time zone as well
The simplest (and flexible) way to get timezone from the epoch is by using POSIX::strftime. It takes the list from localtime and returns a string generated according to the given format.
The %z specifier produces the timezone as the UTC offset, while %Z produces the (notorious and unportable) short name. See your system's strftime manpage for details. Example
use POSIX qw(strftime);
say strftime "%z %Z", localtime; #--> -0700 PDT
(thanks to ikegami's answer which proded me to add the timezone discussion)
Using the incremental parsing feature of JSON parsers, one can safely parse sequences of JSON documents such as the one you have with very little code. This means there's no point in hacking together a JSON parser using regex matches.
use Cpanel::JSON::XS qw( );
my $decoder = Cpanel::JSON::XS->new();
while (<>) {
$decoder->incr_parse($_);
while ( my $rec = $decoder->incr_parse() ) {
say sprintf "%-11s %s",
$rec->{newState},
format_ts($rec->{setAt});
}
}
Complete program:
#!/usr/bin/perl
use strict;
use warnings;
use feature qw( say );
use utf8;
use open ':std', ':encoding(UTF-8)';
use Cpanel::JSON::XS qw( );
use POSIX qw( strftime );
sub format_ts {
my ($ts) = #_;
my $ms = $ts % 1000;
my $epoch = ( $ts - $ms ) / 1000;
my #lt = localtime($epoch);
return sprintf("%s.%03d %s",
strftime("%a %b %d %H:%M:%S", #lt),
$ms,
strftime("%Y %z", #lt),
);
}
my $decoder = Cpanel::JSON::XS->new();
while (<>) {
$decoder->incr_parse($_);
while ( my $rec = $decoder->incr_parse() ) {
say sprintf "%-11s %s",
$rec->{newState},
format_ts($rec->{setAt});
}
}
Output:
runnable Mon Apr 20 18:19:19.359 2020 -0400
running Mon Apr 20 18:21:22.891 2020 -0400
debug_hold Mon Apr 20 18:33:34.895 2020 -0400
terminating Mon Apr 20 19:19:48.577 2020 -0400
failed Mon Apr 20 19:19:56.544 2020 -0400
Note that I added time zone information because the timestamps would be ambiguous without it (because of overlaps when switching from daylight-saving time to standard time). I also showed how you can keep the milliseconds if you so desire.
A small perl script can process such data with ease
USAGE: script_name.pl timestamps.json
#!/usr/bin/perl
use strict;
use warnings;
my($state,$time);
while(<>) {
chomp;
$state = $1 if /"newState": "(.*)"/;
$time = $1 if /"setAt": (\d+)/;
printf "%-12s %s\n", $state, "".localtime($time/1000) if /}/;
}
Alternative version
use strict;
use warnings;
my $data = do { local $/; <> };
my %state = $data =~ /"newState": "(.*?)".*?"setAt": (\d+)/sg;
while(my($s,$t) = each %state) {
printf "%-12s %s\n", $s, "".localtime($t/1000);
}
Input file timestamps.json
{
"newState": "runnable",
"setAt": 1587421159359
}
{
"newState": "running",
"setAt": 1587421282891
}
{
"newState": "debug_hold",
"setAt": 1587422014895
}
{
"newState": "terminating",
"setAt": 1587424788577
}
{
"newState": "failed",
"setAt": 1587424796544
}
Output
runnable Mon Apr 20 15:19:19 2020
running Mon Apr 20 15:21:22 2020
debug_hold Mon Apr 20 15:33:34 2020
terminating Mon Apr 20 16:19:48 2020
failed Mon Apr 20 16:19:56 2020
I have a huge file with data in the below format. (It's the response from an API call I made to one of Twitter's APIs). I want to extract the value of the field "followers_count" from it. Ordinarily, I would do this with jq with the following command : cat | jq -r '.followers_count'
But this contains special characters so jq cannot handle it. Can someone help by telling me how do I convert it in JSON (e.g. using a shell script) or alternatively how to get the followers_count field without conversion? If this format has a specific name, I would be interested to know about it.
Thanks.
SAMPLE LINE IN FILE:
b'[{"id":2361407554,"id_str":"2361407554","name":"hakimo ait","screen_name":"hakimo_ait","location":"","description":"","url":null,"entities":{"description":{"urls":[]}},"protected":false,"followers_count":0,"friends_count":6,"listed_count":0,"created_at":"Sun Feb 23 19:08:04 +0000 2014","favourites_count":0,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":1,"lang":"fr","status":{"created_at":"Sun Feb 23 19:09:21 +0000 2014","id":437665498961293312,"id_str":"437665498961293312","text":"c.ronaldo","truncated":false,"entities":{"hashtags":[],"symbols":[],"user_mentions":[],"urls":[]},"source":"\u003ca href=\"https:\/\/mobile.twitter.com\" rel=\"nofollow\"\u003eMobile Web (M2)\u003c\/a\u003e","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"geo":null,"coordinates":null,"place":null,"contributors":null,"is_quote_status":false,"retweet_count":0,"favorite_count":0,"favorited":false,"retweeted":false,"lang":"es"},"contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"C0DEED","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_tile":false,"profile_image_url":"http:\/\/abs.twimg.com\/sticky\/default_profile_images\/default_profile_normal.png","profile_image_url_https":"https:\/\/abs.twimg.com\/sticky\/default_profile_images\/default_profile_normal.png","profile_link_color":"1DA1F2","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"has_extended_profile":false,"default_profile":true,"default_profile_image":true,"following":false,"follow_request_sent":false,"notifications":false,"translator_type":"none"}]'
This is not the valid JSON, if you want to grab some certain part from this response, you can dump this result in file and then iterate over it and get the text you want to grab.
otherwise, if response will be in JSON, it will be easily parse through jq library, you can also dump this record in file, convert it into json and then parse it !
there are multiple ways 'grep,awk,sed' ..... you can go for it !
remove 'b from beginning and ' from bottom,it will become valid JSON !
Well i have removed the b' from beginning and ' from the bottom ! and look it is a valid JSON, now we can easily use jq with it like this !
i am doing it with my file....
jq -r '.accounts|keys[]' ../saadaccounts.json | while read key ;
do
DATA="$(jq ".accounts [$key]" ../saadaccounts.json )"
FNAME=$( echo $DATA | jq -r '.first_name' )
LNAME=$( echo $DATA | jq -r '.Last_name' )
done
*** YOUR JSON FILE ***
[
{
"id":2361393867,
"id_str":"2361393867",
"name":"graam a7bab",
"screen_name":"bedoo691",
"location":"",
"description":"\u0627\u0633\u062a\u063a\u0641\u0631\u0627\u0644\u0644\u0647 \u0648\u0627\u062a\u0648\u0628 \u0627\u0644\u064a\u0647\u0647 ..!*",
"url":null,
"entities":{
"description":{
"urls":[
]
}
},
"protected":false,
"followers_count":1,
"friends_count":6,
"listed_count":0,
"created_at":"Sun Feb 23 19:03:21 +0000 2014",
"favourites_count":1,
"utc_offset":null,
"time_zone":null,
"geo_enabled":false,
"verified":false,
"statuses_count":7,
"lang":"ar",
"status":{
"created_at":"Tue Mar 04 16:07:44 +0000 2014",
"id":440881284383256576,
"id_str":"440881284383256576",
"text":"#Naif8989",
"truncated":false,
"entities":{
"hashtags":[
],
"symbols":[
],
"user_mentions":[
{
"screen_name":"Naif8989",
"name":"\u200f naif alharbi",
"id":540343286,
"id_str":"540343286",
"indices":[
0,
9
]
}
],
"urls":[
]
},
"source":"\u003ca href=\"http:\/\/twitter.com\/download\/android\" rel=\"nofollow\"\u003eTwitter for Android\u003c\/a\u003e",
"in_reply_to_status_id":437675858485321728,
"in_reply_to_status_id_str":"437675858485321728",
"in_reply_to_user_id":2361393867,
"in_reply_to_user_id_str":"2361393867",
"in_reply_to_screen_name":"bedoo691",
"geo":null,
"coordinates":null,
"place":null,
"contributors":null,
"is_quote_status":false,
"retweet_count":0,
"favorite_count":0,
"favorited":false,
"retweeted":false,
"lang":"und"
},
"contributors_enabled":false,
"is_translator":false,
"is_translation_enabled":false,
"profile_background_color":"C0DEED",
"profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png",
"profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png",
"profile_background_tile":false,
"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/437664693373911040\/ydODsIeh_normal.jpeg",
"profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/437664693373911040\/ydODsIeh_normal.jpeg",
"profile_link_color":"1DA1F2",
"profile_sidebar_border_color":"C0DEED",
"profile_sidebar_fill_color":"DDEEF6",
"profile_text_color":"333333",
"profile_use_background_image":true,
"has_extended_profile":false,
"default_profile":true,
"default_profile_image":false,
"following":false,
"follow_request_sent":false,
"notifications":false,
"translator_type":"none"
}
]
while working with json in windows is very easy in Linux I'm getting trouble.
I found a way to convert list into json using jq:
For example:
ls | jq -R -s -c 'split("\n")'
output:
["bin","boot","dev","etc","home","lib","lib64","media","mnt","opt","proc","root","run","sbin","srv","sys","tmp","usr","var"]
I'm getting trouble to convert a table into json
I'm looking for an option to convert a table that I get from bash command into a json. I already searched for many tools but none of them are generic and you need to adjust the commands to each different table.
Do you know how can I convert a table that I get from a bash commands into json that can be generic?
table output for example:
rpm -qai
output:
Name : gnome-session
Version : 3.8.4
Release : 11.el7
Architecture: x86_64
Install Date: Mon 21 Dec 2015 04:12:41 PM EST
Group : User Interface/Desktops
Size : 1898331
License : GPLv2+
Signature : RSA/SHA256, Thu 03 Jul 2014 09:39:10 PM EDT,
Key ID 24c6a8a7f4a80eb5
Source RPM : gnome-session-3.8.4-11.el7.src.rpm
Build Date : Mon 09 Jun 2014 09:12:26 PM EDT
Build Host : worker1.bsys.centos.org
Relocations : (not relocatable)
Packager : CentOS BuildSystem <http://bugs.centos.org>
Vendor : CentOS
URL : http://www.gnome.org
Summary : GNOME session manager
Description : nome-session manages a GNOME desktop or GDM login session. It starts up the other core GNOME components and handles logout and saving the
session.
Thanks!
There are too many poorly-specified textual formats to create a single tool for what you are asking for, but Unix is well-equipped to the task. Usually, you would create a simple shell or Awk script to convert from one container format to another. Here's one example:
printf '"%s", ' * | sed 's/, $//;s/.*/[ & ]/'
The printf will produce a comma-separated, double-quoted list of wildcard matches. The sed will trim the final comma and add a pair of square brackets around the entire output. The results will be incorrect if a file name contains a double quote, for example, but in the name of simplicity, let's not embellish this any further.
Here's another:
rpm -qai | awk -F ' *: ' 'BEGIN { print "{\n"; }
{ printf "%s\"%s\": \"%s\"", delim, $1, substr($0, 15); delim="\n," }
END { print "\n}"; }'
The -qf output format is probably better but this shows how you can extract fields from a reasonably free-form line-oriented format using a simple Awk script. The first field before the colon is extracted as the key, and everything from the 15th column onwards is extracted as the value. Again, we ignore the possible complications (double quotes in the values would need to be escaped, again, for example) to keep the example simple.
If your needs are serious, you will need to spend more time on creating a robust parser; but then, you will usually want to work with tools which have a well-defined output format in the first place (XML, JSON, etc) and spend as little time as possible on ad-hoc parsers. Unfortunately, there is still a plethora of tools out there which do not support an --xml or --json output option out of the box, but JSON support is fortunately becoming more widely supported.
You can convert a table from bash command into a json using jq
This command will return a detailed report on the system’s disk space usage
df -h
The output is something like this
Filesystem Size Used Avail Capacity iused ifree %iused Mounted on
/dev/disk3s1s1 926Gi 20Gi 803Gi 3% 502068 4293294021 0% /
devfs 205Ki 205Ki 0Bi 100% 710 0 100% /dev
/dev/disk3s6 926Gi 7.0Gi 803Gi 1% 7 8418661400 0% /System/Volumes/VM
/dev/disk3s2 926Gi 857Mi 803Gi 1% 1811 8418661400 0% /System/Volumes/Preboot
/dev/disk3s4 926Gi 623Mi 803Gi 1% 267 8418661400 0% /System/Volumes/Update
Now we can convert the output of this command into json with jq
command=($(df -h | tr -s ' ' | jq -c -Rn 'input | split(" ") as $head | inputs | split(" ") | to_entries | map(.key = $head[.key]) | from_entries'))
echo $command | jq
{
"Filesystem": "/dev/disk3s1s1",
"Size": "926Gi",
"Used": "20Gi",
"Avail": "803Gi",
"Capacity": "3%",
"iused": "502068",
"ifree": "4293294021",
"%iused": "0%",
"Mounted": "/"
}
{
"Filesystem": "devfs",
"Size": "205Ki",
"Used": "205Ki",
"Avail": "0Bi",
"Capacity": "100%",
"iused": "710",
"ifree": "0",
"%iused": "100%",
"Mounted": "/dev"
}
{
"Filesystem": "/dev/disk3s6",
"Size": "926Gi",
"Used": "7.0Gi",
"Avail": "803Gi",
"Capacity": "1%",
"iused": "7",
"ifree": "8418536520",
"%iused": "0%",
"Mounted": "/System/Volumes/VM"
}
{
"Filesystem": "/dev/disk3s2",
"Size": "926Gi",
"Used": "857Mi",
"Avail": "803Gi",
"Capacity": "1%",
"iused": "1811",
"ifree": "8418536520",
"%iused": "0%",
"Mounted": "/System/Volumes/Preboot"
}
convert table from bash command into json