I have got a unix(aix) command which includes a small awk script. It works and here it is...
ps -eaf | awk 'ARGIND == 1 {$pids[$0] = 1} ARGIND > 1 {if ($2 in pids) printf("%s\n",$0)}' /home/richard/myFile.flg -
When I run this command from a different box using ssh it doesn't work.
ssh myuser#myOtherBox ps -eaf | awk 'ARGIND == 1 {$pids[$0] = 1} ARGIND > 1 {if ($2 in pids) printf("%s\n",$0)}' /home/richard/myFile.flg -
I've worked out that I need to quote the awk script and escape some characters in the awk command but I can't get the escapes right.
Would someone pls help me with quoting the awk part of the script and escaping what is required.
thanks
What happens when you execute
ssh myuser#myOtherBox ps -eaf | ...
is that ps -eaf is run on the other box, and the output is returned; ssh then writes the output it receives to its own stdout, which is (locally) redirected through the command ...; in this case, an awk command.
Unfortunately, (I assume) /home/richard/myFile.flg is on the remote mache and not the local machine, so the awk command fails.
To get the whole thing to run on the remote machine, you need to provide it as a single argument; one way which doesn't require much quoting effort is to use a here-doc:
ssh myuser#myOtherBox "$(cat<<"END"
ps -eaf |
awk 'ARGIND == 1 {pids[$0] = 1}
ARGIND > 1 {if ($2 in pids) printf("%s\n",$0)}' \
/home/richard/myFile.flg -
END
)"
Note that printf("%s\n",$0) is really just a complicated way of writing print, so you could simplify the remote command quite a bit. But you would still need to deal with the single quotes in the awk command:
ssh myuser#myOtherBox '
ps -eaf |
awk '"'"'ARGIND == 1 {pids[$0] = 1; next}
$2 in pids {print}'"'"' \
/home/richard/myFile.flg -'
To understand '"'"', you need to break it into pieces:
' close '-quoted string
"'" A (quoted) '
' open another '-quoted string
In cases like this you need double escaping, this should work:
ssh myuser#myOtherBox "ps -eaf | awk \"ARGIND == 1 {\\\$pids[\\\$0] = 1} ARGIND > 1 {if (\\\$2 in pids) printf(\\\"%s\n\\\",\\\$0)}\" /home/richard/myFile.flg -"
If you can use bash's $'STRING' syntax, then things remain quite
readable; in this case one only has to escape the single-quotes and
backslashes:
$'ps -eaf |
awk \'
ARGIND == 1 {$pids[$0] = 1}
ARGIND > 1 {if ($2 in pids) printf("%s\\n",$0)}\' /home/richard/myFile.flg -'
Related
I would like to modify several shell variables within awk:
echo "$LINE_IN" | awk '/pattern1/ {print $0; WRITTEN=1; REC=$REC+1}' >> $FILE1
I tried to put eval, but still does not work:
eval $( echo "$LINE_IN" | awk '/pattern1/ {print $0; WRITTEN=1; REC=$REC+1}' >> $FILE1 )
Any suggestion?
I would like to use k-shell script, thanks!
Count the hits when you are finished:
echo "${LINE_IN}" | grep -E 'pattern1' > "${FILE1}"
REC=$(wc -l < "${FILE1}")
if (( REC > 0 )); then
WRITTEN=1
fi
When you really want to use awk, you must let awk write the results to stdout and parse stdout:
echo "${LINE_IN}" | awk '/echo/ {print $0 > "x3"; WRITTEN=1; REC++}
END { print "WRITTEN=" WRITTEN; print "REC=" REC}'
WRITTEN=1
REC=6
And when you want the variables really set, wrap it:
source (echo "${LINE_IN}" | awk '/echo/ {print $0 > "x3"; WRITTEN=1; REC++}
END { print "WRITTEN=" WRITTEN; print "REC=" REC}')
Note: Get used to using lowercase variable names like written, file and rec.
I wanted to merge a string ABC to a line that I have extracted from a file.
The following command is used to extract the lines 20-25 in file_ABC, take only the first column, which is then transposed to become a row (or line).
sed -n '20,25p' < file_ABC | awk '{print $1}' | paste -s
This is the result:
2727778 14734 0 0 0 2713044
I would like to add at the first position of this line the string ABC.
ABC 2727778 14734 0 0 0 2713044
Any suggestion on how to do that?
A quick hack would be to use something like
printf 'ABC\t%s\n' "$(sed -n '20,25p' < file_ABC | awk '{print $1}' | paste -s)"
You could modify your initial command instead to use awk for everything, though:
awk '
BEGIN {printf "ABC"}
NR>=20 && NR<=25 {printf "\t%s", $1}
END {print ""}
' file_ABC
This might work for you (GNU sed):
sed '20,25{s/\s.*//;H};$!d;x;s/^/ABC/;s/\n/ /g' file
Gather up the first column fields by appending them to the hold space for rows 20 to 25 only. At the end of the file prepend ABC and replace the introduced newlines by spaces.
For fun, bash only
filename=file_ABC
words=("${filename##*_}")
i=0
while read -r word rest_of_line; do
((++i < 20 )) && continue
(( i > 25 )) && break
words+=("$word")
done < "$filename"
join() { local IFS=$1; shift; echo "$*"; }
join $'\t' "${words[#]}"
But this will be much slower than a single awk call.
if you want to keep all in one script
$ awk 'BEGIN {line="ABC"}
NR>=20 && NR<=25 {line=line FS $1}
NR==25 {print line; exit}' file
improved version as suggested by #EdMorton
$awk 'NR>=20 {line=line OFS $1}
NR==25 {print "ABC" line; exit}' file
$ echo foooobazbarrrrr |
> gawk 'match($0, /(fo+).+(bar*)/, arr)
> {print arr[1], arr[2] }'
The output of this code should be foooo barrrr but on my Ubuntu, it is not working and failed.
If I wrote this code
> gawk 'match($0, /(fo+).+(bar*)/)
> {print }'
Then its working. Why is the first version not working?
Your command is slightly different from the example in the GNU manual. It has the opening { at the very start so that there's no pattern to match and the newline is required to separate the two awk commmands.
$ echo foooobazbarrrrr | gawk '{ match($0, /(fo+).+(bar*)/, arr)
> print arr[1], arr[2] }'
foooo barrrrr
Alternatively, you could use a semi-colon instead of a newline to separate the commands:
$ echo foooobazbarrrrr | gawk '{ match($0, /(fo+).+(bar*)/, arr); print arr[1], arr[2] }'
foooo barrrrr
Your version of the command will work if it’s entered as one line:
$ echo foooobazbarrrrr | gawk 'match($0, /(fo+).+(bar*)/, arr) {print arr[1], arr[2] }'
foooo barrrrr
I have 10 devices that using hp-ux and i want to check the disk space in each devices.
my requirement is if the space more than 90%, the info of device ans space will be save to a log.
this is list of device and ip address which i set as variable ipadd:
lo1 100.45.32.43
lot2 100.45.32.44
lot3 100.45.32.44
lot4 100.45.32.45
lot5 100.45.32.46
and so on..
This is my script so far :
#!/bin/csh -f
set ipaddress = (`awk '{print $2}' "ipadd"`)
set device = (`awk '{print $1}' "ipadd"`)
# j = 1
while ($j <= $#ipaddress)
echo $ipaddress
set i = 90 # Threshold set at 90%
set max = 100
while ($i <= $max)
rsh $ipaddress[$j] bdf | grep /dev/vg00 | grep $i% \
|awk '{ file=substr($6,index($6,"/") + 1,length($6)); print "WARNING: $device[$j]:/" file " has reached " $5 ". Perform HouseKeeping IMMEDIATELY..." >> "/scripts/space." file ".file"}'
# i++
end
# j++
end
The output after bdf:
/dev/vg00/lvol2 15300207 10924582 28566314 79% /
/dev/vg00/lvol4 42529 23786 25510 55% /stand
The output at terminal after exec the script:
100.45.32.43
100.45.32.44
The output at .file:
WARNING: $device[$j]:/ has reached 79%. Perform HouseKeeping IMMEDIATELY...
My question is, is it my looping have something wrong cause only iterates one time only because my .file output only show one device only?
And why $device[$j] not come out in .file output?
or awk have problem?
Thank you for the advice.
Your code tested for each possible percentage between 90 and 100.
Persumably, you'd be OK with code that checks once, and asks 'is device percent greater than 90%'?. So then you don't need the inner loop at all, and you make only 1 connection per machine, try
#!/bin/csh -f
set ipaddress = (`awk '{print $2}' "ipadd"`)
set device = (`awk '{print $1}' "ipadd"`)
# j = 1
set i = 90 # Threshold set at 90%
while ($j <= $#ipaddress)
echo $ipaddress
echo "#dbg: ipaddress[$j]=${ibpaddress[$j]}"
rsh $ipaddress[$j] bdf \
| awk -v thresh="$i" -v dev="$device[$j]" \
'/\/dev\/vg00/ { \
sub(/%/,"",$5) \
if ($5 > thresh) { \
file=substr($6,index($6,"/") + 1,length($6)) \
print "WARNING: " dev ":/" file " has reached " $5 ". Perform HouseKeeping IMMEDIATELY..." >> "/scripts/space." file ".file" \
}\
}'
# j++
end
Sorry, but I don't have a csh available to dbl-chk for syntax errors.
So here is a one liner that we determined worked in your environment.
rsh $ipaddress[$j] bdf | nawk -v thresh="$i" -v dev="$device[$j]" '/\/dev\/vg00/ { sub(/%/,"",$5) ; if ($5 > thresh) { file=substr($6,index($6,"/") + 1,length($6));print "#dbg:file="file; print "WARNING: " dev ":/" file " has reached " $5 ". Perform HouseKeeping IMMEDIATELY..." >> "/scripts/space.file.TMP" } }'
I don't have a system with bdf available. Change the two references to $5 in the sub() and if test to match the field-number of the output that has the percentage you want to test.
Note that -v var="value" is the standard way to pass a variable value from the shell to an awk script that is enclosed in single-quotes.
Be careful that any '\' chars at the end of a line are the last chars, no trailing space or tabs, or you'll get an indecipherable error msg. ;-)
IHTH
This was an interview question, nevertheless still a programming question.
I have a unix file with two columns name and score. I need to display count of all the scores.
like
jhon 100
dan 200
rob 100
mike 100
the output should be
100 3
200 1
You only need to use built in unix utility to solve it, so i am assuming using shell scripts . or reg ex. or unix commands
I understand looping would be one way to do. store all the values u have already seen and then grep every record for unseen values. any other efficient way of doing it
Try this:
cut -d ' ' -f 2 < /tmp/foo | sort -n | uniq -c \
| (while read n v ; do printf "%s %s\n" "$v" "$n" ; done)
The initial cut could be replaced with another while read loop, which would be more resilient to input file format variations (extra whitespace). If some of the names consist in several words, simple field extraction will not work as easily, but sed can do it.
Otherwise, use your favorite programming language. Perl would probably shine. It is not difficult either in Java or even in C or Forth.
$ cat foo.txt
jhon 100
dan 200
rob 100
mike 100
$ awk '{print $2}' foo.txt | sort | uniq -c
3 100
1 200
Its a pity you can't do a count with sort or uniq alone.
Edit: I just noticed I have the count in front ... to get it exactly the same you can do:
$ awk '{print $2}' foo.txt | sort | uniq -c | awk '{ print $2 " " $1 }'
Not very complicated in perl:
#!/usr/bin/perl -w
use strict;
use warnings;
my %count = ();
while (<>) {
chomp;
my ($name, $score) = split(/ /);
$count{$score}++;
}
foreach my $key (sort keys %count) {
print "$key ", $count{$key}, "\n";
}
You could go with awk:
awk '/.*/ { a[$2] = a[$2] + 1; } END { for (x in a) { print x, " ", a[x] } }' record_file.txt
Alternatively with shell commands:
for i in `awk '{print $2}' inputfile | sort -u`
do
echo -n "$i "
grep $i inputfile | wc -l
done
The first awk command will give a list of all the different scores (e.g. 100 and 200) which then
the for loop iterates over, counting up each separately. Not very super efficient, but simple. If the file is not to big is should not be a too big problem.