The following is the output of the command ads2 cls create
kernel with pid 7148 (port 9011) killed
kernel with pid 9360 (port 9011) killed
probing service daemon # http://fdt-c-vm-0093.fdtech.intern:9010
starting kernel FDT-C-VM-0093 # http://fdt-c-yy-0093.ssbt.intern:9011 name=FDT-C-VM-0093 max_consec_timeouts=10 clustermode=Standard hostname=FDT-C-VM-0093 framerate=20000 schedmode=Standard rtaddr=fdt-c-vm-0093.fdtech.ssbt tickrole=Local tickmaster=local max_total_timeouts=1000
kernel FDT-C-VM-0093 running
probing service daemon # http://172.16.xx.xx:9010
starting kernel FDT-C-AGX-0004 # http://172.16.xx.xx:9011 name=FDT-C-AGX-0004 max_consec_timeouts=10 clustermode=Standard hostname=FDT-C-AGX-0004 framerate=20000 schedmode=Standard rtaddr=172.16.xx.xx tickrole=Local tickmaster=local max_total_timeouts=1000
kernel Fxx-x-xxx-xxx4 running
>>> start cluster establish ...
>>> cluster established ...
nodes {
node {
name = "FDT-C-VM-xxxx";
address = "http://fxx-x-xx-0093.xxx.intern:xxxx/";
state = "3";
}
node {
name = "xxx-x-xxx-xxx";
address = "http://1xx.16.xx.xx:9011/";
state = "3";
}
}
However, I'm trying to extract the value of name and state for each node, save them in a variable and display them in the following order:
**#example output**
Node fdt-c-agx-xxx has state 3
Node FDT-C-VM-xxx has state 3
Till now i, with the help of this so much powerful learning site, could extract the values of name and state by executing the following:
cls="$(ads2 cls create | grep '\(state\|name\) =' | cut -d '"' -f 2)"
Now if i print cls variable, i get the following:
FDT-C-VM-xxx
3
FDT-C-AGX-xxx
3
First Question:
How can i display the result like the one above in the **#example output**?
Second question
With this implementation, how can I check the value of state varaiable for both nodes in order to print something like
if node1State = 3 && node2State = 3; then
echo "Sucess"
else
echo "Failed"
Since the output seems to be json, you should really use a json parser such as jq but in the absence of jq you can use awk and combine the requirements for question one and two:
ads2 cls create | awk -F [\"] '/^>>> cluster established .../ { strt=1 } strt!=1 { next } $1 ~ "name" { cnt++;nam[cnt]=$2 } $1 ~ "state" { stat[cnt]=$2;print "Node "nam[cnt]" has state "$2 } END { if (stat[1]=="3" && stat[2]=="3") { print "Success" } else { print "Failed" } }'
Explanation:
ads2 cls create | awk -F [\"] ' # Set the field delimiter to a double quote
/^>>> cluster established .../ {
strt=1 # If the line starts with ">>> cluster established ...", set a variable strt to 1
}
strt!=1 {
next # If strt is not equal to 1, skip to the next line
}
$1 ~ "name" {
cnt++; # If the first field contains name, increment a cnt variable
nam[cnt]=$2 # Use the cnt variable as the index of an array called nam with the second field the value
}
$1 ~ "state" {
stat[cnt]=$2; # When the first field contains "state", set up another array called stat
print "Node "nam[cnt]" has state "$2 # Print the node name as well as the state
}
END {
if (stat[1]=="3" && stat[2]=="3") {
print "Success" # At the end of processing, use the array to determine whether there is a success of failure.
}
else {
print "Failed"
}
}'
Related
Question: To add a string before and after, if the specified string is matched between patterns using sed in bash ??
In the below code, I want to add /* one line above object Host "kali" { and add */ to the next line after the occurrence of } (not to the last occurrence of }).
This is my code
object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}
object Host "kali" {
import "linux"
address = "linux"
groups = [linux ]
}
object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}
This is the expected output:
object Host "linux" {
import "windows"
address = "linux"
groups = ["linux"]
}
/*
object Host "kali" {
import "linux"
address = "linux"
groups = [linux ]
}
*/
object Host "windows" {
import "linux"
address = "linux"
groups = ["windows" ]
}
**This is what I tried**
#! /bin/bash
NONE='\033[00m'
RED='\033[01;31m'
GREEN='\033[0;32m'
clear
echo -e "Enter the names to comment in config file"
cat > comment-file.txt
clear
echo -e "#################################################################################"
echo "Please wait. The names will be commented shortly............"
echo -e "#################################################################################"
echo "Dont press any button, Please hold on...."
while read -r names
do
loc=$(grep -il "object.*Host.*\"$names.*\"" /home/jo/folders/test-sc/*.txt)
if [ -z $loc ]
then
echo -e " $names$RED No Object definition found $NONE "
else
sed -i '/object Host \"$names.*\" {/ { s,^,/*\n,
: loop
/}/ {
s,$,\n*/,
p
d
}
N
b loop
}' "$loc"
echo -e " $names - $loc - $GREEN Object host defenition commented $NONE "
fi
done < comment-file.txt
echo -e "#################################################################################"
echo -e "\t\t\t\t Script completed \t\t\t\t"
echo -e "#################################################################################"
rm -rf comment-file.txt
Error:
No changes had been made in the output file which means /home/jo/folders/test-sc/*.txt
This might work for you (GNU sed):
sed -e '/object Host "kali"/{i\/*' -e ':a;n;/}/!ba;a\*/' -e '}' file
Look for a line containing object Host "kali" insert a line before it containing /*, read/print further lines until one containing } and append the line */.
After finding object Host "kali" {:
Prepend /*\n to the pattern space
If } is seen, append \n*/ to the pattern space, print and delete
Append Next line to the pattern space and loop back to step 2.
sed -e '/object Host "kali" {/ {
s,^,/*\n,
: loop
/}/ {
s,$,\n*/,
p
d
}
N
b loop
}'
.... addendum... To properly pass "$names" to be a part of the sed script, we will need to follow the quoting rules for sh ... the idea will be to embedding "$names" into the sed script, and the sed line will look like the following:
sed -i -e "/object Host \"$names\" {/ {
s,^,/*\n,
: loop
/}/ {
s,$,\n*/,
p
d
}
N
b loop
}" "$loc"
So, I am trying to get information out of OS X's "favorite servers" .plist so that I can then decide whether or not I want to add certain servers to it. Some of the information for how this can be done can be found here:
http://jacobsalmela.com/bash-script-set-favorite-servers-in-connect-to-menu/
The problem with this is that you can't, for example, just do
/usr/libexec/Plistbuddy -c "Add favoriteservers:CustomListItems:0:Name string server1.fqdn.com" com.apple.sidebarlists.plist
over and over, because Plistbuddy is not smart enough to do an insert into the array. You have to know how long the array is, and then add things to the end of it, such that when you go to add things you have already determined whether you need to use 0 or 5 or 7 between "CustomListItems" and "Name" up there.
The obnoxiousness of that aside, I'm having trouble just parsing the output from the Plistbuddy print command, which looks like this:
Array { Dict { Name = afp://or-fs-001/vol1 URL = afp://or-fs-001/vol1 } Dict { Name = smb://or-fs-001/vol1 URL = smb://or-fs-001/vol1 } Dict { Name = vnc://or-fs-001/vol1 URL = vnc://or-fs-001/vol1 } Dict { Name = ftp://or-fs-001/vol1 URL = ftp://or-fs-001/vol1 } }
So you have the same URL twice for each entry (I have no idea why there is both a "Name" and "URL" when you can't actually make them different), and they may start with any protocol supported by Finder, which means afp, http, https, smb, or vnc. The first thing I'm trying to do is just split them up into pieces by the "Name" substring so that I know how many entries are in the list, but that results in weird behavior when I use tr for that; it starts cutting out way too many pieces.
Does anyone have ideas for better ways to do this? Can I count the number of times "Dict" shows up?
You can use grep -o to extract interesting parts of the input. An example:
#!/bin/bash
output='Array { Dict { Name = afp://or-fs-001/vol1 URL = afp://or-fs-001/vol1 } Dict { Name = smb://or-fs-001/vol1 URL = smb://or-fs-001/vol1 } Dict { Name = vnc://or-fs-001/vol1 URL = vnc://or-fs-001/vol1 } Dict { Name = ftp://or-fs-001/vol1 URL = ftp://or-fs-001/vol1 } }'
count=$(echo "$output" | grep -o 'Name =' | wc -l)
names=($(grep -o 'Name = [^ ]\+' <<< "$output" | cut -f3- -d' '))
echo $count = ${#names[#]}
for name in "${names[#]}" ; do
echo "$name"
done
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
Kindly help me detecting the error in the following code
proc strcmp { d1 d2 } {
set res [string compare $d1 $d2]
switch $res
0 {
puts "String is equal "
}
1 {
puts "$d1 > $d2"
} default {
puts "$d2 > $d1"
}
}
I get this error message when i try to execute in tcl 8.5
wrong # args: should be "switch ?switches? string pattern body ... ?default body?"
You have the "0" on a different line. Tcl treats newlines as command terminators. You're probably seeing an error like "0: no such command"
Use either line continuations
switch $res \
0 {
puts "String is equal "
} \
1 {
puts "$d1 > $d2"
} \
default {
puts "$d2 > $d1"
}
or enclosing brackets (good for readability)
switch $res {
0 {
puts "String is equal "
}
1 {
puts "$d1 > $d2"
}
default {
puts "$d2 > $d1"
}
}
Docs:
http://tcl.tk/man/tcl8.6/TclCmd/Tcl.htm (rules 1 and 9)
http://tcl.tk/man/tcl8.6/TclCmd/switch.htm
The exact error I got when I tried to run your code was:
bad option "-1": must be -exact, -glob, -regexp, or -- while executing
The Tcl docs for switch mention these commands: they tell the switch how to match the arguments. Adding -exact -- before $res at the start of the switch statement seems to make it work:
proc strcmp { d1 d2 } {
set res [string compare $d1 $d2]
switch -exact -- $res {
0 {
puts "String is equal "
}
1 {
puts "$d1 > $d2"
}
default {
puts "$d2 > $d1"
}
}
}
strcmp "world" "hello"
strcmp "hello" "hello"
strcmp "hello" "world"
(You could drop the -exact if you want, as it's the default – I only include it so you can see where to put in another option.)
This gives the following output (codepad example):
world > hello
String is equal
world > hello
so for some reason, -1 (the return code from string compare for the third example) is getting matched to 1 by the switch statement. I don't know enough Tcl to know why that might be the case – you could consider asking that as a different question.
I want to extract lines before and after a matched pattern.
eg: if the file contents are as follows
absbasdakjkglksagjgj
sajlkgsgjlskjlasj
hello
lkgjkdsfjlkjsgklks
klgdsgklsdgkldskgdsg
I need find hello and display line before and after 'hello'
the output should be
sajlkgsgjlskjlasj
hello
lkgjkdsfjlkjsgklks
This is possible with GNU but i need a method that works in AIX / KSH SHELL WHERE NO GNU IS INSTALLED.
sed -n '/hello/{x;G;N;p;};h' filename
I've found it is generally less frustrating to build the GNU coreutils once, and benefit from many more features http://www.gnu.org/software/coreutils/
Since you'll have Perl on the machine, you could use the following code, but you'd probably do better to install the GNU utilities. This has options -b n1 for lines before and -f n1 for lines following the match. It works with PCRE matches (so if you want case-insensitive matching, add an i after the regex instead using a -i option. I haven't implemented -v or -l; I didn't need those.
#!/usr/bin/env perl
#
# #(#)$Id: sgrep.pl,v 1.7 2013/01/28 02:07:18 jleffler Exp $
#
# Perl-based SGREP (special grep) command
#
# Print lines around the line that matches (by default, 3 before and 3 after).
# By default, include file names if more than one file to search.
#
# Options:
# -b n1 Print n1 lines before match
# -f n2 Print n2 lines following match
# -n Print line numbers
# -h Do not print file names
# -H Do print file names
use warnings;
use strict;
use constant debug => 0;
use Getopt::Std;
my(%opts);
sub usage
{
print STDERR "Usage: $0 [-hnH] [-b n1] [-f n2] pattern [file ...]\n";
exit 1;
}
usage unless getopts('hnf:b:H', \%opts);
usage unless #ARGV >= 1;
if ($opts{h} && $opts{H})
{
print STDERR "$0: mutually exclusive options -h and -H specified\n";
exit 1;
}
my $op = shift;
print "# regex = $op\n" if debug;
# print file names if -h omitted and more than one argument
$opts{F} = (defined $opts{H} || (!defined $opts{h} and scalar #ARGV > 1)) ? 1 : 0;
$opts{n} = 0 unless defined $opts{n};
my $before = (defined $opts{b}) ? $opts{b} + 0 : 3;
my $after = (defined $opts{f}) ? $opts{f} + 0 : 3;
print "# before = $before; after = $after\n" if debug;
my #lines = (); # Accumulated lines
my $tail = 0; # Line number of last line in list
my $tbp_1 = 0; # First line to be printed
my $tbp_2 = 0; # Last line to be printed
# Print lines from #lines in the range $tbp_1 .. $tbp_2,
# leaving $leave lines in the array for future use.
sub print_leaving
{
my ($leave) = #_;
while (scalar(#lines) > $leave)
{
my $line = shift #lines;
my $curr = $tail - scalar(#lines);
if ($tbp_1 <= $curr && $curr <= $tbp_2)
{
print "$ARGV:" if $opts{F};
print "$curr:" if $opts{n};
print $line;
}
}
}
# General logic:
# Accumulate each line at end of #lines.
# ** If current line matches, record range that needs printing
# ** When the line array contains enough lines, pop line off front and,
# if it needs printing, print it.
# At end of file, empty line array, printing requisite accumulated lines.
while (<>)
{
# Add this line to the accumulated lines
push #lines, $_;
$tail = $.;
printf "# array: N = %d, last = $tail: %s", scalar(#lines), $_ if debug > 1;
if (m/$op/o)
{
# This line matches - set range to be printed
my $lo = $. - $before;
$tbp_1 = $lo if ($lo > $tbp_2);
$tbp_2 = $. + $after;
print "# $. MATCH: print range $tbp_1 .. $tbp_2\n" if debug;
}
# Print out any accumulated lines that need printing
# Leave $before lines in array.
print_leaving($before);
}
continue
{
if (eof)
{
# Print out any accumulated lines that need printing
print_leaving(0);
# Reset for next file
close ARGV;
$tbp_1 = 0;
$tbp_2 = 0;
$tail = 0;
#lines = ();
}
}
I had a situation where I was stuck with a slow telnet session on a tablet, believe it or not, and I couldn't write a Perl script very easily with that keyboard. I came up with this hacky maneuver that worked in a pinch for me with AIX's limited grep. This won't work well if your grep returns hundreds of lines, but if you just need one line and one or two above/below it, this could do it. First I ran this:
cat -n filename |grep criteria
By including the -n flag, I see the line number of the data I'm seeking, like this:
2543 my crucial data
Since cat gives the line number 2 spaces before and 1 space after, I could grep for the line number right before it like this:
cat -n filename |grep " 2542 "
I ran this a couple of times to give me lines 2542 and 2544 that bookended line 2543. Like I said, it's definitely fallable, like if you have reams of data that might have " 2542 " all over the place, but just to grab a couple of quick lines, it worked well.