awk: "not enough arguments to satisfy format string" error in script - shell

I created a script to grab the data from our Unix server, however I am getting the below error:
awk: cmd. line:8: (FILENAME=- FNR=2) fatal: not enough arguments to satisfy format string
`|%-17s|%-16s|%-15s|'
^ ran out for this one
Below is the complete script:
#!/bin/sh
export TERM=xterm
ipath=/usr/local/nextone/bin
date=$(date +"%Y%m%d%H%M")
ifile="$(date '+/var/EndpointUsage_%I-%M-%p_%d-%m-%Y.csv')"
"$ipath"/cli iedge list | awk '
BEGIN { print "|-----------------|------------------|------------------|";
printf "|%-18s|%-17s|%-16s|\r\n","Registration ID", "Port", "Ongoing Calls"
}
/Registration ID/ { id = $3; next }
/Port/ { port = $3 ; next }
/Ongoing Calls/ {print "|-------------------|-----------------|------------- -----|";
printf "|%-18s|%-17s|%-16s|\r\n",id,port,$3 }
END{
print "|------------------|------------------|------------------|";
}'>> "$ifile"
Can anyone please help me on this, how can I resolve this error?
AFTER CHANGES the columns are showing correctly, but the Port column does not have any data. It should have 0 or if other endpoint have 1 o 2 Port number.
|-----------------|------------------|------------------|
|Registration ID |Port |Ongoing Calls |
|-------------------|-----------------|------------------|
|-------------------|-----------------|------------------
|CC_XXXXXX_01_0 | |174 |
|-------------------|-----------------|------------------|

The offending printf is:
printf "|%-18s|%-17s|%-16s|\r\n",id,$3
^^^^ awk wants to see a third parameter here
You have three %s sequences in the format string, so awk expects ,<something else> after the $3. I think maybe it's a copy and paste error. Since you are only printing two column headers, try removing the %-16s| at the end and seeing if that gives you the output you expect.
Edit Without seeing your input file, I don't know for sure. Try this, though -
/Registration ID/ { id = $3; next }
/Port/ { port = $3 ; next }
/Ongoing Calls/ {print "|-------------------|-----------------|------------------|";
printf "|%-18s|%-17s|%-16s|\r\n",id,port,$3 }
I added {port=$3;next} to save the port number, and then when you print them out, I changed id,$3 to id,port,$3 to print the saved id, saved port, and ongoing-calls value ($3) in order.

Related

awk or other shell to convert delimited list into a table

So what I have is a huge csv akin to this:
Pool1,Shard1,Event1,10
Pool1,Shard1,Event2,20
Pool1,Shard2,Event1,30
Pool1,Shard2,Event4,40
Pool2,Shard1,Event3,50
etc
Which is not ealisy readable. Eith there being only 4 types of events I'm useing spreadsheets to convert this into the following:
Pool1,Shard1,10,20,,
Pool1,Shard2,30,,,40
Pool2,Shard1,,,50,
Only events are limited to 4, pools and shards can be indefinite really. But the events may be missing from the lines - not all pools/shards have all 4 events every day.
So I tried doing this within an awk in the shell script that gathers the csv in the first place, but I'm failing spectacuraly, no working code can even be shown since it's producing zero results.
Basically I tried sorting the CSV reading the first two fields of a row, comparing to previous row and if matching comparing the third field to a set array of event strings then storing the fouth field in a variable respective to the event, and one the first two fileds are not matching - finally print the whole line including variables.
Sorry for the one-liner, testing and experimenting directly in the command line. It's embarassing, it does nothing.
awk -F, '{if (a==$1&&b==$2) {if ($3=="Event1") {r=$4} ; if ($3=="Event2") {d=$4} ; if ($3=="Event3") {t=$4} ; if ($3=="Event4") {p=$4}} else {printf $a","$b","$r","$d","$p","$t"\n"; a=$1 ; b=$2 ; if ($3=="Event1") {r=$4} ; if ($3=="Event2") {d=$4} ; if ($3=="Event3") {t=$4} ; if ($3=="Event4") {p=$4} ; a=$1; b=$2}} END {printf "\n"}'
You could simply use an assoc array: awk -F, -f parse.awk input.csv with parse.awk being:
{
sub(/Event/, "", $3);
res[$1","$2][$3]=$4;
}
END {
for (name in res) {
printf("%s,%s,%s,%s,%s\n", name, res[name][1], res[name][2], res[name][3], res[name][4])
}
}
Order could be confused by awk, but my test output is:
Pool2,Shard1,,,50,
Pool1,Shard1,10,20,,
Pool1,Shard2,30,,,40
PS: Please use an editor to write awk source code. Your one-liner is really hard to read. Since I used a different approach, I did not even try do get it "right"... ;)
$ cat tst.awk
BEGIN { FS=OFS="," }
{ key = $1 OFS $2 }
key != prev {
if ( NR>1 ) {
print prev, f["Event1"], f["Event2"], f["Event3"], f["Event4"]
delete f
}
prev = key
}
{ f[$3] = $4 }
END { print key, f["Event1"], f["Event2"], f["Event3"], f["Event4"] }
$ sort file | awk -f tst.awk
Pool1,Shard1,10,20,,
Pool1,Shard2,30,,,40
Pool2,Shard1,,,50,

Bash script does nothing when I run it, seems to keep waiting

I've written my first script, one in which I want to know if 2 files have the same values in a specific column.
Both files are WEKA machine-learning prediction outputs for different algorithms, hence they have to be in the same format, but the prediction column would be different.
Here's the code I've written based on the tutorial presented in https://linuxconfig.org/bash-scripting-tutorial-for-beginners:
#!/bin/bash
lineasdel1=$(wc -l $1 | awk '{print $1}')
lineasdel2=$(wc -l $2 | awk '{print $1}')
if [ "$lineasdel1" != "$lineasdel2" ]; then
echo "Files $1 and $2 have different number of lines, unable to perform"
exit 1
fi
function quitalineasraras {
awk '$1!="==="&&NF>0'
}
function acomodo {
awk '{gsub(/^ +| +$/, ""); gsub(/ +0/, " W 0"); gsub(/ +1$/, " W 1"); gsub(/ +/, "\t") gsub(/\+\tW/, "+"); print}'
}
function procesodel1 {
quitalineasraras "$1" | acomodo
}
function procesodel2 {
quitalineasraras "$2" | acomodo
}
el1procesado=$(procesodel1)
el2procesado=$(procesodel2)
function pegar {
paste <(echo "$el1procesado") <(echo "$el2procesado")
}
function contarintersec {
awk 'BEGIN {FS="\t"} $3==$8 {n++} END {print n}'
}
unido=$(pegar)
interseccion=$(contarintersec $unido)
echo "Estos 2 archivos tienen $interseccion coincidencias."
I ran all individual codes of all functions in the terminal and verified they work successfully (I'm using Linux Mint 19.2). Script's permissions also have been changed to make it executable. Paste command also is supposed to work with that variable syntax.
But when I run it via:
./script.sh file1 file2
if both files have the same number of lines, and I press enter, no output is obtained; instead, the terminal opens an empty line with cursor waiting for something. In order to write another command, I've got to press CTRL+C.
If both files have different number of lines the error message prints successfully, so I think the problem has something to do with the functions, with the fact that awk has different syntax for some chores, or with turning the output of functions into variables.
I know that I'm missing something, but can't come up with what could be.
Any help will be appreciated.
what could be.
function quitalineasraras {
awk '$1!="==="&&NF>0'
}
function procesodel1 {
quitalineasraras "$1" | acomodo
}
el1procesado=$(procesodel1)
The positional variables $1 are set for each function separately. The "$1" inside procesodel1 expands to empty. The quitalineasraras is passed one empty argument "".
The awk inside quitalineasraras is passed only the script without the filename, so it reads the input for standard input, ie. it waits for the input on standard input.
The awk inside quitalineasraras without any file arguments makes your script seem to wait.

Assign the value of awk-for loop variable to a bash variable

content within the tempfile
123 sam moore IT_Team
235 Rob Xavir Management
What i'm trying to do is get input from user and search it in the tempfile and output of search should give the column number
Code I have for that
#!/bin/bash
set -x;
read -p "Enter :" sword6;
awk 'BEGIN{IGNORECASE = 1 }
{
for(i=1;i<=NF;i++) {
if( $i ~ "'$sword6'$" )
print i;
}
} ' /root/scripts/pscripts/tempprint.txt;
This exactly the column number
Output
Enter : sam
2
What i need is the value of i variable should be assigned to bash variable so i can call as per the need in script.
Any help in this highly appreciated.
I searched to find any existing answer but not able to find any. If any let me know please.
first of all, you should pass your shell variable to awk in this way (e.g. sword6)
awk -v word="$sword6" '{.. if($i ~ word)...}`...
to assign shell variable by the output of other command:
shellVar=$(awk '......')
Then you can continue using $shellVar in your script.
regarding your awk codes:
if user input some special chars, your script may fail, e.g .*
if one column had matched userinput multiple times, you may have duplicated output.
if your file had multi-columns matching user input, you may want to handle it.
You just need to capture the output of awk. As an aside, I would pass sword6 as an awk variable, not inject it via string interpolation.
i=$(awk -v w="$sword6" '
BEGIN { IGNORECASE = 1 }
{ for (i=1;i<=NF;i++) {
if ($i ~ w"$") { print i; }
}
}' /root/scripts/pscipts/tempprint.txt)
Following script may help you on same too.
cat script.ksh
echo "Please enter the user name:"
read var
awk -v val="$var" '{for(i=1;i<=NF;i++){if(tolower($i)==tolower(val)){print i,$i}}}' Input_file
If tempprint.txt is big
awk -v w="$word6" '
BEGIN { IGNORECASE = 1 }
"$0 ~ \\<w\\>" {
for(i=1;i<=NF;i++)
if($i==w)print i
}' tempprint.txt

Awk Command analyze and explain

I have tried reading some tutorial about awk script, but still have doubt with below scripts:
get_value_from_ini()
{
section=$1
key_name=$2
echo `awk -F '=' '/\['"$section"'\]/{a=1}a==1&&$1~/'"$key_name"'/{gsub(/[[:blank:]]*/,"",$2); print $2}' $cfg_file`
}
db_user=`get_value_from_ini DB DBUSER`
db_passwd=`get_value_from_ini DB DBPASSWD`
And input config ini file like:
....
[DB]
DBUSER=dbuser
DBPASSWD=dbpasswd
...
As I unserstand, in awk command, the pattern is "[DB]", and field seperator is '=', but it worked correctly and can return correct result, Why ? Could you help me understand it ?
Thanks a lot!
Although poorly written code you simply read from left to right:
-F '=' -- As you have said, set FS to =
'/\'"$section"'\]/{a=1} -- If the line contains the information stored in variable section then set a=1
a==1 && $1~/'"$key_name"'/ -- If 'a' set and first field contains the information stored in variable key_name, execute all between {}
{gsub(/[[:blank:]]*/,"",$2); print $2}' -- If previous step returns true, remove all whitespace from second field and then print it out
$cfg_file -- file to be read
Here is an alternative to get rid of all the extra quotes which make it very confusing to read and become possibly error prone:
get_value_from_ini()
{
awk -F '=' -vsection="$1" -vkey_name="$2" '$0 ~ "["section"]"{a=1}a==1 && $1 ~ key_name{gsub(/[[:blank:]]*/,"",$2); print $2}' $cfg_file`
}

How to send an E-mail with AWK using the SYSTEM command

So I have this uni homework where I need to send an E-mail to all the people from a certain group of students (which are listed in the diak.lst file) and to attach the text from a file to the mail as well. The address of the students is in a similar form: xy9999, 2 characters and 4 numbers.
The problem seems to be at the SYSTEM part, it says that there is no such thing as "attachedfile".
The diak.lst is in the following format:
F-Name,Name,Address,Group
George Peter gp9999 511
This is the script
#!/bin/bash
if [ ! -f $2 ]
then echo $2 Not a file!!
exit 1
fi
awk -v group=$1 -v attachedfile=$2 'BEGIN {fs=" ";nr_sent_to=0;}
{
if ($4==group){
system("mail -s \"Attention!\"" $3 " < " attachedfile);
nr_sent_to++;
}
}
END {
print nr_sent_to,"-have received the mail";
}
' diak.lst
You dont have a space before $3 in the string mail sees- try changing \"Attention!\"" $3 to \"Attention!\" " $3.
You have fs=" " which does nothing. Maybe you mean FS=" " but that's the default value so it will do nothing.
Your input file has a comma-separated header line so when processing that $3 etc. will be blank, add a NR>1 clause.
awk vars already init to 0/NULL so no need for nr_sent_to=0. You do have to then add +0 when printing it in the END section to make sure the number zero instead of the NULL string is printed if/when no emails are sent.
Given that everything you are doing in the BEGIN section is doing nothing useful, get rid of the BEGIN section.
Won't affect the script execution but: awk is not C - lose all the useless trailing semi-colons.
Put conditions in the condition part of the script, not the action part.
Quote your shell variables.
So with a bit more cleanup the end result would be:
awk -v group="$1" -v attachedfile="$2" '
NR>1 && ($4==group) {
system("mail -s \"Attention!\" " $3 " < " attachedfile)
nr_sent_to++
}
END {
print nr_sent_to+0, "-have received the mail"
}
' diak.lst

Resources