I need to generate JSON output from my shell script.
I need to get Ram slot details of a particular machine and generate JSON using those details.
To get Ram details I am using system_profiler SPMemoryDataType
It produces details as follows.
BANK 0/DIMM0:
Size: 2 GB
Type: DDR3
Speed: 1600 MHz
Status: OK
Manufacturer: 0x802C
Part Number: 0x384A54463235363634485A2D3147364D3120
Serial Number: 0xE98388E6
BANK 1/DIMM0:
Size: 2 GB
Type: DDR3
Speed: 1600 MHz
Status: OK
Manufacturer: 0x802C
Part Number: 0x384A54463235363634485A2D3147364D3120
Serial Number: 0xE98388E5
From that I should form JSON like this
[
{"Bank":"0/DIMM0","Serial Number":"0xE98388E6","Status":"OK"},
{"Bank":"1/DIMM0","Serial Number":"0xE98388E5","Status":"OK"}
]
To extract separate details like bank, Serial Number, Status we can use
system_profiler SPMemoryDataType | awk '/Bank/
system_profiler SPMemoryDataType | awk '/Serial/
system_profiler SPMemoryDataType | awk '/Status/
I am sure that there is a need of Dynamic variable to do form json from the results. But since I am new to shell script I am confused. Is there any way to generate JSON from the output?
#!/usr/bin/awk -f
$1 == "BANK" {
bank = $2
sub(/:/, "", bank)
while (getline > 0) {
if ($1 == "Serial" && $2 == "Number:") {
serial_number = $3
} else if ($1 == "Status:") {
status = $2
}
if (serial_number != "" && status != "") {
entries[++e] = "{\"Bank\":\"" bank "\",\"Serial Number\":\"" serial_number "\",\"Status\":\"" status "\"}"
break
}
}
bank = serial_number = status = ""
}
END {
print "["
if (e > 0) {
printf "%s", entries[1]
for (i = 2; i <= e; ++i) {
printf ",\n%s", entries[i]
}
print ""
}
print "]"
}
Usage:
awk -f script.awk file
system_profiler SPMemoryDataType | awk -f script.awk
Example output:
[
{"Bank":"0/DIMM0","Serial Number":"0xE98388E6","Status":"OK"},
{"Bank":"1/DIMM0","Serial Number":"0xE98388E5","Status":"OK"}
]
Using within a shell script:
#!/bin/bash
system_profiler SPMemoryDataType | awk '$1 == "BANK" {
bank = $2
sub(/:/, "", bank)
while (getline > 0) {
if ($1 == "Serial" && $2 == "Number:") {
serial_number = $3
} else if ($1 == "Status:") {
status = $2
}
if (serial_number != "" && status != "") {
entries[++e] = "{\"Bank\":\"" bank "\",\"Serial Number\":\"" serial_number "\",\"Status\":\"" status "\"}"
break
}
}
bank = serial_number = status = ""
}
END {
print "["
if (e > 0) {
printf "%s", entries[1]
for (i = 2; i <= e; ++i) {
printf ",\n%s", entries[i]
}
print ""
}
print "]"
}'
A one-liner:
system_profiler SPMemoryDataType | awk '$1=="BANK"{bank=$2;sub(/:/,"",bank);while(getline>0){if($1=="Serial"&&$2=="Number:"){serial_number=$3}else if($1=="Status:"){status=$2};if(serial_number!=""&&status!=""){entries[++e]="{\"Bank\":\""bank"\",\"SerialNumber\":\""serial_number"\",\"Status\":\""status"\"}";break}};bank=serial_number=status=""}END{print "[";if(e>0){printf "%s",entries[1];for(i=2;i<=e;++i){printf ",\n%s",entries[i]};print""};print "]"}'
There are some few libraries to do so.. One such thing is https://github.com/jeganathgt/libjson-sh . It is standalone shell script library, provides easy handy API's to generate json output in console.
Ex :
json_init
json_add_string "serial" "$<cmd>"
json_add_string "bankinfo" "$<cmd>"
json_add_string "status" "$<cmd>"
json_dump
Related
I have the following scenario I have a block of text and example
basketball:
ball: round
being that I don't know exactly what's inside basketball: but I like to delete everything inside it example:
men:
height: 170
athlete: basketball
women:
height:180
athlete: basketball
I want to delete only the men block ignoring whatever is above or below this key
The AWK script filter.awk below removes all men sections which contains basketball. Is that what you mean? Run with awk -f filter.awk input.txt.
/^[A-Za-z0-9]/ {
if (sectionWanted) {
printf "%s", section
}
sectionWanted = 1
section = ""
sectionName = $1
}
/basketball/ && sectionName == "men:" {
sectionWanted = 0
}
{
section = section $0 "\n"
}
END {
if (sectionWanted) {
printf "%s", section
}
}
I'm trying to create my own program to do a recursive listing: each line corresponds to the full path of a single file. The tricky part I'm working on now is: I don't want bind mounts to trick my program into listing files twice.
So I already have a program that produces the right output except that if /foo is bind mounted to /bar then my program incorrectly lists
/foo/file
/bar/file
I need the program to list just what's below (EDIT: even if it was asked to list the contents of /foo)
/bar/file
One approach I thought of is to mount | grep bind | awk '{print $1 " " $3}' and then iterate over this to sed every line of the output, then sort -u.
My question is how do I iterate over the original output (a bunch of lines) and the output from mount (another bunch of lines)? (or is there a better approach) This needs to be POSIX (EDIT: and work with /bin/sh)
Place the 'mount | grep bind' command into the AWK within a BEGIN block and store the data.
Something like:
PROG | awk 'BEGIN{
# Define the data you want to store
# Assign to global arrays
command = "mount | grep bind";
while ((command | getline) > 0) {
count++;
mount[count] = $1;
mountPt[count] = $3
}
}
# Assuming input is line-by-line and that mountPt is the value
# that is undesired
{
replaceLine=0
for (i=1; i<=count; i++) {
idx = index($1, mountPt[i]);
if (idx == 1) {
replaceLine = 1;
break;
}
}
if (replaceLine == 1) {
sub(mountPt[i], mount[i], $1);
}
if (printed[$1] != 1) {
print $1;
}
printed[$1] = 1;
} '
Where I assume your current program, PROG, outputs to stdout.
find YourPath -print > YourFiles.txt
mount > Bind.txt
awk 'FNR == NR && $0 ~ /bind/ {
Bind[ $1] = $3
if( ( ThisLevel = split( $3, Unused, "/") - 1 ) > Level) Level = ThisLevel
}
FNR != NR && $0 !~ /^ *$/ {
RealName = $0
for( ThisLevel = Level; ThisLevel > 0; ThisLevel--){
match( $0, "(/[^/]*){" ThisLevel "}" )
UnBind = Bind[ substr( $0, 1, RLENGTH) ]
if( UnBind !~ /^$/) {
RealName = UnBind substr( $0, RLENGTH + 1)
ThisLevel = 0
}
}
if( ! File[ RealName]++) print RealName
}
' Bind.txt YourFiles.txt
search based on a exact path/bind comparaison from a bind array loaded first
Bind.txt and YourFiles.txt could be a direct redirection to be "1" instruction and no temporary files
have to be adapted (first part of awk) if path in bind are using space character (assume not here)
file path are changed live when reading, compare to an existing bind relation
print file if not yet known
I want to sort data from shortest to longest line ,the data contains
space ,character ,number,-,","
,i use sort -n ,but it did not solve the job.many thanks for help
Data here
0086
0086-
0086---
0086-------
0086-1358600966
0086-18868661318
00860
00860-13081022659
00860-131111111
00860-13176880028
00860-13179488252
00860-18951041771
00861
008629-83023520
0086000
0086010-61281306
and the rerult i want is
0086
0086-
00860
00861
0086000
0086---
0086-------
0086-1358600966
00860-131111111
008629-83023520
0086-18868661318
0086010-61281306
00860-13081022659
00860-13176880028
00860-13179488252
00860-18951041771
I do not care what characters ,just from short to long .2 lines with the same long can exchange ,it is not a problem .many thanks
Perl one-liner
perl -0777 -ne 'print join("\n", map {$_->[1]} sort {$a->[0] <=> $b->[0]} map {[length, $_]} split /\n/), "\n"' file
Explanation on demand.
With GNU awk, it's very simple:
gawk '
{len[$0] = length($0)}
END {
PROCINFO["sorted_in"] = "#val_num_asc"
for (line in len) print line
}
' file
See https://www.gnu.org/software/gawk/manual/html_node/Controlling-Scanning.html#Controlling-Scanning
Just try this once, May be it will help you.
awk '{ print length($0) " " $0; }' $file | sort -n | cut -d ' ' -f 2-
the -r option was for reversing the sort.
Using awk:
#!/usr/bin/awk -f
(l = length($0)) && !($0 in nextof) {
if (l in start) {
nextof[$0] = start[l]
} else {
if (!max || l > max) max = l
if (!min || l < min) min = l
nextof[$0] = 0
}
start[l] = $0
++count[l]
}
END {
for (i = min; i <= max; ++i) {
if (j = count[i]) {
t = start[i]
print t
while (--j) {
t = nextof[t]
print t
}
}
}
}
Usage:
awk -f script.awk file
Output:
0086
00861
00860
0086-
0086000
0086---
0086-------
008629-83023520
00860-131111111
0086-1358600966
0086010-61281306
0086-18868661318
00860-18951041771
00860-13179488252
00860-13176880028
00860-13081022659
Another Version:
#!/usr/bin/awk -f
(l = length($0)) && !($0 in nextof) {
if (l in start) {
nextof[lastof[l]] = $0
} else {
if (!max || l > max) max = l
if (!min || l < min) min = l
start[l] = $0
}
lastof[l] = $0
++count[l]
}
END {
for (i = min; i <= max; ++i) {
if (j = count[i]) {
t = start[i]
print t
while (--j) {
t = nextof[t]
print t
}
}
}
}
Output:
0086
0086-
00860
00861
0086---
0086000
0086-------
0086-1358600966
00860-131111111
008629-83023520
0086-18868661318
0086010-61281306
00860-13081022659
00860-13176880028
00860-13179488252
00860-18951041771
I have a shell script that is doing something.I want to print the Unknown string where there is blank space in the output.
I want to do check if (f[1] == "") or (f[2] == "") or (f[3] == ""), it should be replaced by a unknown string and should be written in a single file
if(f[1] == "") printf(fmt, id, f[1], f[2], f[3]) > file
where f[1],f[2],f[3] if empty should be replaced by unknown string
where f[1] is the first index, fmt is the format specifier I have defined in the code.How to replace these empty spaces with a string in Linux.
Any lead is appreciated.
Thanks
Use the conditional operator:
ec2-describe-instances | awk -F'\t' -v of="$out" -v mof="$file" '
function pr() { # Print accumulated data
if(id != "") { # Skip if we do not have any unprinted data.
printf(fmt, id, f[1], f[2], f[3]) > of
if (f[1] == "" || f[2] == "" || f[3] == "") {
printf(fmt, id, f[1]==""?"Unknown":f[1], f[2]==""?"Unknown":f[2], f[3]==""?"Unknown":f[3]) > mof
}
}
# Clear accumulated data.
id = f[1] = f[2] = f[3] = ""
}
BEGIN { # Set the printf() format string for the header and the data lines.
fmt = "%-20s %-40s %-33s %s\n"
# Print the header
headerText="Instance Details"
headerMaxLen=100
padding=(length(headerText) - headerMaxLen) / 2
printf("%" padding "s" "%s" "%" padding "s" "\n\n\n", "", headerText, "") > of
printf(fmt, "Instance id", "Name", "Owner", "Cost.centre") > of
printf("%" padding "s" "%s" "%" padding "s" "\n\n\n", "", headerText, "") > mof
printf(fmt, "Instance id", "Name", "Owner", "Cost.centre") > mof
}
$1 == "TAG" {
# Save the Instance ID.
id = $3
if($4 ~ /[Nn]ame/) fs = 1 # Name found
else if($4 ~ /[Oo]wner/) fs = 2 # Owner found
else if($4 ~ /[Cc]ost.[Cc]ent[er][er]/) fs = 3 # Cost center found
else next # Ignore other TAGs
f[fs] = $5 # Save data for this field.
}
$1 == "RESERVATION" {
# First line of new entry found; print results from previous entry.
pr()
}
END { # EOF found, print results from last entry.
pr()
}'
I am brand new to AWK and trying to determine if my array is empty or not so i can print a message if so. Typically i am use to length functions and can check like that, but it does not seem AWK has those. Here is my working code, i just want to print out a different message if there is nothing in the array after parsing all my data.
#add to array if condition is met
if ($2 == "SOURCE" && $4 == "RESTRICTED"){
sourceAndRestricted[$3]++;
}
#print out array
for (var in sourceAndRestricted){
printf "\t\t"var"\n"
}
ive tried something like this and its not working. Suggestions?
for (var in sourceAndRestricted){
if (var > 1){
printf "\t\t"var"\n"
}
else {
print "NONE"
}
}
Check it with length() function:
if ( length(sourceAndRestricted) > 0 ) {
printf "\t\t"var"\n"
}
else
print "NONE"
}
$ cat tst.awk
function isEmpty(arr, idx) {for (idx in arr) return 0; return 1}
BEGIN {
map[3] = 27
print isEmpty(map)
delete map[3]
print isEmpty(map)
}
$ awk -f tst.awk
0
1