Asked this question earlier, trying to figure out where to place " in system command and one user had suggested to use escape characters as below, but still getting the syntax error. Any leads appreciated!
free -m | awk 'NR==2{
if (($3*100/$2)>=10.00){
printf system("ps aux --sort=-%mem | awk \'NR==2{print $2}\'");
}
else
{
printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 ;
}
}'
As tripleee's comment too, I believe we could use a different mechanism (may be in shell for calculations) and then could write something, calling awk inside awk is not that cool to be honest. Could you please try following once.
free -m | awk -v s1="'" 'NR==2{
if (($3*100/$2)>=10.00){
printf system("ps aux --sort=-%mem | awk " s1 "NR==2{print $2}" s1 );
}
else
{
printf("Memory Usage: %s/%sMB %.2f%%\n", $3,$2,$3*100/$2)
}
}'
EDIT: In case one don't want to use variable for storing ' then use Octal representation of it as follows.
free -m | awk 'NR==2{
if (($3*100/$2)>=10.00){
printf system("ps aux --sort=-%mem | awk \047 NR==2{print $2} \047" );
}
else
{
printf("Memory Usage: %s/%sMB %.2f%%\n", $3,$2,$3*100/$2)
}
}'
Here is a different approach using a single awk with process substitution:
awk 'NR == FNR {
if (FNR == 2)
if ($3*100/$2 > 10)
p=1
else
printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2
next
}
p && FNR == 2 {
print $2
exit
}' <(free -m) <(ps aux --sort=-%mem)
Related
Basically, I need to check a file to see whether a username occurs more than once and if it does, place that username into a different file.
In the end the script is meant to check every 3 seconds whether a user is logged in more than once and if they are, add their name to a file called user.deny.
here's what I have basically at the moment:
who -q > temp.file
if [ user occurs more than once in temp.file ]; then
that username > user.deny
fi
I just want to know a way to check the temp file for all words/names which occur more than once. I can't use grep because that requires a pattern.
Thanks in advance.
You can do this using sort, uniq and awk:
who | awk '{print $1} '| sort | uniq -c | awk '{ if($1 > 1) {print $2} }' >> user.deny
If you want to do some checking before adding the username, you can wrap it in a loop:
for u in $(who | awk '{print $1} '| sort | uniq -c | awk '{ if($1 > 1) {print $2} }')
do
grep "$u" user.deny || echo "$u" >> user.deny
done
Using Ruby
$ who |
ruby -ane 'BEGIN { h = Hash.new(0) };
h[$F[0]] += 1;
END { puts h.select { |k,v| v > 1 }.keys.join ?\n }' > user.deny
I am trying to count strings containing a number at the end in a large data file, and for this use the "for i loop" to search all of them consecutively. Here is my code:
#!/bin/bash
for (( i=2; i<=253; i++ ))
do
awk -F "\t" '$3 ~ /^names.i$/ {++c} END {print c}' myfile >> output.txt
done
For some reason although using awk only gives the right output, the script produces just empty spaces in shell. What do I do wrong?
Just do the whole thing in 1 awk invocation:
awk -F '\t' '
{ split($3,arr,/\./); ++c[arr[2]] }
END { for (i=2;i <= 253;i++) print c[i]+0 }
' myfile > output.txt
You can't use shell variable i directly in awk like that. Pass it to awk first:
for (( i=2; i<=253; i++ ))
do
awk -v i=$i -F "\t" '$3 ~ "^names\." i "$" {++c} END {print c}' myfile >> output.txt
done
Try this
awk -F "\t" '{for (i=2;i<=253;i++) if ($3 ~ /^names.i$/) ++c} END {print c}' myfile
I'm using the following awk command:
my_command | awk -F "[[:space:]]{2,}+" 'NR>1 {print $2}' | egrep "^[[:alnum:]]"
which successfully returns my data like this:
fileName1
file Name 1
file Nameone
f i l e Name 1
So as you can see some file names have spaces. This is fine as I'm just trying to echo the file name (nothing special). The problem is calling that specific row within a loop. I'm trying to do it this way:
i=1
for num in $rows
do
fileName=$(my_command | awk -F "[[:space:]]{2,}+" 'NR==$i {print $2}' | egrep "^[[:alnum:]])"
echo "$num $fileName"
$((i++))
done
But my output is always null
I've also tried using awk -v record=$i and then printing $record but I get the below results.
f i l e Name 1
EDIT
Sorry for the confusion: rows is a variable that list ids like this 11 12 13
and each one of those ids ties to a file name. My command without doing any parsing looks like this:
id File Info OS
11 File Name1 OS1
12 Fi leNa me2 OS2
13 FileName 3 OS3
I can only use the id field to run a the command that I need, but I want to use the File Info field to notify the user of the actual File that the command is being executed against.
I think your $i does not expand as expected. You should quote your arguments this way:
fileName=$(my_command | awk -F "[[:space:]]{2,}+" "NR==$i {print \$2}" | egrep "^[[:alnum:]]")
And you forgot the other ).
EDIT
As an update to your requirement you could just pass the rows to a single awk command instead of a repeatitive one inside a loop:
#!/bin/bash
ROWS=(11 12)
function my_command {
# This function just emulates my_command and should be removed later.
echo " id File Info OS
11 File Name1 OS1
12 Fi leNa me2 OS2
13 FileName 3 OS3"
}
awk -- '
BEGIN {
input = ARGV[1]
while (getline line < input) {
sub(/^ +/, "", line)
split(line, a, / +/)
for (i = 2; i < ARGC; ++i) {
if (a[1] == ARGV[i]) {
printf "%s %s\n", a[1], a[2]
break
}
}
}
exit
}
' <(my_command) "${ROWS[#]}"
That awk command could be condensed to one line as:
awk -- 'BEGIN { input = ARGV[1]; while (getline line < input) { sub(/^ +/, "", line); split(line, a, / +/); for (i = 2; i < ARGC; ++i) { if (a[1] == ARGV[i]) {; printf "%s %s\n", a[1], a[2]; break; }; }; }; exit; }' <(my_command) "${ROWS[#]}"
Or better yet just use Bash instead as a whole:
#!/bin/bash
ROWS=(11 12)
while IFS=$' ' read -r LINE; do
IFS='|' read -ra FIELDS <<< "${LINE// +( )/|}"
for R in "${ROWS[#]}"; do
if [[ ${FIELDS[0]} == "$R" ]]; then
echo "${R} ${FIELDS[1]}"
break
fi
done
done < <(my_command)
It should give an output like:
11 File Name1
12 Fi leNa me2
Shell variables aren't expanded inside single-quoted strings. Use the -v option to set an awk variable to the shell variable:
fileName=$(my_command | awk -v i=$i -F "[[:space:]]{2,}+" 'NR==i {print $2}' | egrep "^[[:alnum:]])"
This method avoids having to escape all the $ characters in the awk script, as required in konsolebox's answer.
As you already heard, you need to populate an awk variable from your shell variable to be able to use the desired value within the awk script so thi:
awk -F "[[:space:]]{2,}+" 'NR==$i {print $2}' | egrep "^[[:alnum:]]"
should be this:
awk -v i="$i" -F "[[:space:]]{2,}+" 'NR==i {print $2}' | egrep "^[[:alnum:]]"
Also, though, you don't need awk AND grep since awk can do anything grep van do so you can change this part of your script:
awk -v i="$i" -F "[[:space:]]{2,}+" 'NR==i {print $2}' | egrep "^[[:alnum:]]"
to this:
awk -v i="$i" -F "[[:space:]]{2,}+" '(NR==i) && ($2~/^[[:alnum:]]/){print $2}'
and you don't need a + after a numeric range so you can change {2,}+ to just {2,}:
awk -v i="$i" -F "[[:space:]]{2,}" '(NR==i) && ($2~/^[[:alnum:]]/){print $2}'
Most importantly, though, instead of invoking awk once for every invocation of my_command, you can just invoke it once for all of them, i.e. instead of this (assuming this does what you want):
i=1
for num in rows
do
fileName=$(my_command | awk -v i="$i" -F "[[:space:]]{2,}" '(NR==i) && ($2~/^[[:alnum:]]/){print $2}')
echo "$num $fileName"
$((i++))
done
you can do something more like this:
for num in rows
do
my_command
done |
awk -F '[[:space:]]{2,}' '$2~/^[[:alnum:]]/{print NR, $2}'
I say "something like" because you don't tell us what "my_command", "rows" or "num" are so I can't be precise but hopefully you see the pattern. If you give us more info we can provide a better answer.
It's pretty inefficient to rerun my_command (and awk) every time through the loop just to extract one line from its output. Especially when all you're doing is printing out part of each line in order. (I'm assuming that my_command really is exactly the same command and produces the same output every time through your loop.)
If that's the case, this one-liner should do the trick:
paste -d' ' <(printf '%s\n' $rows) <(my_command |
awk -F '[[:space:]]{2,}+' '($2 ~ /^[::alnum::]/) {print $2}')
I have an input file that contains:
123,apple,orange
123,pineapple,strawberry
543,grapes,orange
790,strawberry,apple
870,peach,grape
543,almond,tomato
123,orange,apple
i want the output to be:
The following numbers are repeated:
123
543
is there a way to get this output using awk; i'm writing the script in solaris , bash
sed -e 's/,/ , /g' <filename> | awk '{print $1}' | sort | uniq -d
awk -vFS=',' \
'{KEY=$1;if (KEY in KEYS) { DUPS[KEY]; }; KEYS[KEY]; } \
END{print "Repeated Keys:"; for (i in DUPS){print i} }' \
< yourfile
There are solutions with sort/uniq/cut as well (see above).
If you can live without awk, you can use this to get the repeating numbers:
cut -d, -f 1 my_file.txt | sort | uniq -d
Prints
123
543
Edit: (in response to your comment)
You can buffer the output and decide if you want to continue. For example:
out=$(cut -d, -f 1 a.txt | sort | uniq -d | tr '\n' ' ')
if [[ -n $out ]] ; then
echo "The following numbers are repeated: $out"
exit
fi
# continue...
This script will print only the number of the first column that are repeated more than once:
awk -F, '{a[$1]++}END{printf "The following numbers are repeated: ";for (i in a) if (a[i]>1) printf "%s ",i; print ""}' file
Or in a bit shorter form:
awk -F, 'BEGIN{printf "Repeated "}(a[$1]++ == 1){printf "%s ", $1}END{print ""} ' file
If you want to exit your script in case a dup is found, then you can exit a non-zero exit code. For example:
awk -F, 'a[$1]++==1{dup=1}END{if (dup) {printf "The following numbers are repeated: ";for (i in a) if (a[i]>1) printf "%s ",i; print "";exit(1)}}' file
In your main script you can do:
awk -F, 'a[$1]++==1{dup=1}END{if (dup) {printf "The following numbers are repeated: ";for (i in a) if (a[i]>1) printf "%s ",i; print "";exit(-1)}}' file || exit -1
Or in a more readable format:
awk -F, '
a[$1]++==1{
dup=1
}
END{
if (dup) {
printf "The following numbers are repeated: ";
for (i in a)
if (a[i]>1)
printf "%s ",i;
print "";
exit(-1)
}
}
' file || exit -1
Here is my case.
bash ~]# TIME="2012-05-25 06:42:57"
bash ~]# echo "2012-05-25 00:16:51,610" | awk -v var=$TIME '{if ($0 < var) print $0}'
Then, here is the error message
awk: 06:42:57
awk: ^ syntax error
I just want to pass a date range to my awk command. How to archive this? Please help. Thanks.
Modify the case
START_TIME="2012-05-24 00:00:00"
END_TIME="2012-05-24 01:00:00"
echo "2012-05-24 00:10:10" | awk -v "START=$START_TIME" -v "END=$END_TIME" '{ if ( $0 > START && $0 < END) print $0 }'
It seems not working in IF conditions.
awk: { if ( $0 < START && $0 > END) print $0 }
awk: ^ syntax error
After serval trying, seems found the solution with another approach.
echo "2012-05-24 00:10:10" | awk '{ if ( $0 > "'"$START_TIME"'" && $0 < "'"$END_TIME"'" ) print $0 }'
Not sure how to do it with awk variable "-v". Anyone have idears?
Quote your variable when passing it to AWK:
echo "2012-05-25 00:16:51,610" | awk -v "var=$TIME" '{if ($0 < var) print $0}'
At least on my system, it appears that "END" is a reserved word even for variables. Awk uses END as it uses BEGIN, but I hadn't seen attempts to use it as a variable before. Note:
[ghoti#pc ~]$ START_TIME="2012-05-24 00:00:00"
[ghoti#pc ~]$ END_TIME="2012-05-24 01:00:00"
[ghoti#pc ~]$ echo "2012-05-24 00:10:10" | awk -v "START=$START_TIME" -v "END=$END_TIME" '{ if ( $0 < START && $0 > END) print $0 }'
awk: syntax error at source line 1
context is
{ if ( $0 < START && $0 > >>> END <<< ) print $0 }
awk: illegal statement at source line 1
[ghoti#pc ~]$ echo "2012-05-24 00:10:10" | awk -v s_time="$START_TIME" -v e_time="$END_TIME" '{ if ( $0 < s_time && $0 > e_time) print $0 }'
[ghoti#pc ~]$
Obviously this still isn't working, but now it's not working because of a misunderstanding about how comparisons work, rather than because we're trying to use a reserved word as a variable.
Looking at your if statement, it seems that you're trying to evaluate TRUE only if the comparison string is both BEFORE the start date and AFTER the end date. Barring theories of time being circular, I think we can assume that this logic is flawed.
So here's what I came up with. Note that this uses gawk's mktime() function, so it won't work everywhere.
[ghoti#pc ~]$ START_TIME="2012-05-24 00:00:00"
[ghoti#pc ~]$ END_TIME="2012-05-24 01:00:00"
[ghoti#pc ~]$ printf '2012-05-23 22:10:10\n2012-05-24 00:10:10\n2012-05-24 01:10:10\n' | gawk -v s_time="$START_TIME" -v e_time="$END_TIME" 'BEGIN { s=mktime(gensub(/[^0-9]/," ","G",s_time)); e=mktime(gensub(/[^0-9]/," ","G",e_time)); } { now=mktime(gensub(/[^0-9]/," ","G")); if ( now > s && now < e) print $0 }'
2012-05-24 00:10:10
[ghoti#pc ~]$
Spaced out for easier reading, the gawk script looks like this:
BEGIN {
s=mktime(gensub(/[^0-9]/," ","G",s_time));
e=mktime(gensub(/[^0-9]/," ","G",e_time));
}
{
now=mktime(gensub(/[^0-9]/," ","G"));
if ( now > s && now < e) print $0;
}
Obviously, this relies completely on the fact that your date/time specification matches mktime()'s input format so closely. But it works with the sample data in your question.