Error while calling for loop inside awk command - unix

The snippet reads as below:
echo -n "Filename: "
read filename
echo -n "Data Fields? "
read -a ar
awk -F '[[:space:]][[:space:]]+' 'BEGIN{OFS = "--"} {for val in "${ar[#]}" printf $val }' $filename
ar is an array i am reading, filename is name of a file i am reading too.
The file looks as below :
100 Thomas Manager Sales $5,000
200 Jason Developer Technology $5,500
300 Sanjay Sysadmin Technology $7,000
400 Nisha Manager Marketing $9,500
500 Randy DBA Technology $6,000
What i am trying to do is, accept the filename from user, also take the field number he wants to display, and then scan the file and produce the output accordingly.
The array "ar" contains the field numbers, like 1,2,3.
Input is as below:
$ sh awk_prac.sh
Filename: employee.txt
Data Fields: 2 3
Now according to the input i gave above, the output should look like below:
Thomas Manager
Jason Developer
Sanjay Sysadmin
Nisha Manager
Randy DBA
But everytime i run the code, it shows a syntax error pointing under "val" after for.
awk: cmd. line:1: BEGIN{OFS = "--"} {for val in "${ar[#]}" printf $val }
awk: cmd. line:1: ^ syntax error
Can anyone point out the mistake. #i_am_new_to_this#

The problem with your code is explained in the comments under your question (awk is not shell), here's how to solve it:
$ cat tst.sh
echo -n "Filename: "
read filename
echo -n "Data Fields? "
read fields
awk -v fields="$fields" 'BEGIN{n=split(fields,f)} {for (i=1; i<=n; i++) printf "%s%s", $(f[i]), (i<n?OFS:ORS)}' "$filename"
$ ./tst.sh
Filename: file
Data Fields? 2 3
Thomas Manager
Jason Developer
Sanjay Sysadmin
Nisha Manager
Randy DBA

Related

i have to replace all the ? with X in unix except some valid?

It kind of some complicated question. I want to replace all the ? in the file with X. But the problem is there are some valid ? also there in input file.
eg:
input:
HELLO ?, WELCOME TO THE NEW WORLD??23, and you are most ? valid person.
output:
HELLO X, WELCOME TO THE NEW WORLDX?23, and you are most X valid person.
here, ? comes before 23 is valid one.. like ?23, many values are there. ?24,?33,?45, etc.,
I tried with sed script, but able to find the exact command.
Script which i used:
LINE_NUM=0
while IFS= read -r LINE
do
LINE_NUM=$?
EXTRACTED=`echo "${LINE}" |grep '?23' | sed 's|^.*\?23||; s|\?[0-9].*$||'`
if [ -n "$EXTRACTED" ]
then
UPDATED=`echo "$EXTRACTED" | sed 's/?/X/g'`
UPDATED_1=`echo "$UPDATED" | awk '{gsub("/","%",$0); print}'`
if [ $EXTRACTED != $UPDATED ]
then
LATEST_VALUE=`echo "${LINE}" | sed "s|${EXTRACTED}|${UPDATED}|g"`
fi
LATEST_VALUE=`echo "${LINE}"`
echo "$LATEST_VALUE" >> outputfile.txt
else
echo "$LINE" >> outputfile.txt
fi
done<inputfile.txt
$ echo "HELLO ?, WELCOME TO THE NEW WORLD??23, and you are most ? valid person." |
sed -E 's/\?([^0-9]|$)/X\1/g'
HELLO X, WELCOME TO THE NEW WORLDX?23, and you are most X valid person.
here it escapes ? followed by a digit (or end of line). If your list is more restricted change the regex there.
With your shown samples, please try following. In GNU awk please try following. Simple explanation would be, setting record separator as 1 ? followed by 1 digit, using global substitution to substitute ? with X in current records; setting correct output record separator as RT, then print current line.
awk -v RS='[?][0-9]' '{gsub(/\?/,"X");ORS=RT} 1' Input_file

How to change horizontal input to vertical output

I am using UNIX Korn shell. I am trying to create a program in Unix that will search a text file which is shown below:
Last Name:First Name:City:State:Class:Semester Enrolled:Year First Enrolled
Gilman:Randy:Manhattan:KS:Junior:Spring:2010
Denton:Jacob:Rochester:NY:Senoir:Fall:2009
Goodman:Joshua:Warren:MI:Freshman:Summer:2014
Davidson:Blair:Snohomish:WA:Sophmore:Fall:2013
Anderson:Neo:Seattle:WA:Senoir:Spring:2008
Beckman:John:Ft. Polk:LA:Freshman:Spring:2014
Then take that line and cut it out and display it vertically. So if I search Gilman. It would produce:
Gilman
Randy
Manhattan
KS
Junior
Spring
2010
However included in this I should also be able to produce the following layout:
Last Name: Gilman
First Name: Randy
City: Manhattan
State: KS
Class: Junior
Semester Enrolled: Spring
Year First Enrolled: 2010
Now I have figured out most of it which I will display in my code below:
cat<<MENULIST
A - Add Student Information
D - Delete Student Information
M - Modify Student Information
I - Inquiry on a Student
X - Exit
MENULIST
echo -en '\n'
echo -en '\n'
echo " Pleasa choose one of the following: "
#take input from operation
read choice
case $choice in
a|A) ;;
d|D) ;;
m|M) ;;
i|I)
#Create Inguiry Message
clear
echo " Record Inquiry "
echo -en '\n'
echo -en '\n'
echo "What is the last name of the person"
#Gather search parameter
read last_name
grep -i "$last_name" $1 | cut -f 1-7 -d ':' ;;
x|X) echo "The program is ending" ; exit 0;;
*) echo -en '\n' ;echo "Not an acceptable entry." ;echo "Please press enter to try again"; read JUNK | tee -a $2 ; continue
esac
I am just really having trouble on redirecting input to the proper output. There is much more to the program but this is the relevant portion for this problem. Any help would be appreciated.
EDIT:
Ok the final answer is shown below:
while IFS=: read c1 c2 c3 c4 c5 c6 c7 rest ; do
case "$c1" in
"$last_name" )
echo -e "Last Name: $c1\nFirst Name: $c2\nCity: $c3\nState: $c4\nClass: $c5\nSemester Enrolled: $c6\nYear First Enrolled: $c7\n\n"
;;
esac
done < $1
This performed the intended outcome correctly thank you everyone for your help. I still have some other questions but I will make a new topic for that so it doesn't get to jumbled together.
Replace your line
grep -i "$last_name" $1 | cut -f 1-7 -d ':' ;;
with
awk -F: -vnameMatch="$last_name" \
'$1==nameMatch{
printf("LastName:%s\nFirstName:%s\nCity:%s\nState:%s\nClass:%s\nSemester Enrolled:%s\nYear First Enrolled:%s\n\n", \
$1, $2, $3, $4, $5, $6, $7)
}' $1
;;
It's pretty much the same idea in ksh.
while IFS=: read c1 c2 c3 c4 c5 c6 c7 rest ; do
case "$c1" in
"$last_name" )
printf "LastName:%s\nFirstName:%s\nCity:%s\nState:%s\nClass:%s\nSemester Enrolled:%s\nYear First Enrolled:%s\n\n",
"$c1" "$c2" "$c3" "$c4" "$c5" "$c6" "$c7"
;;
esac
done < $1
I think I've got all the syntax right, but don't have the energy tonight to test :-/ ... If you can use this, and there are problems, post a comment and I'll clean it up.
IHTH.
EDIT:
#Case statement for conducting an inquiry of the file
i|I)
#Create Inguiry Message
clear
echo " Record Inquiry "
echo -en '\n'
echo -en '\n'
echo "What is the last name of the person:"
#Gather search parameter
read last_name
while IFS=: read c1 c2 c3 c4 c5 c6 c7 rest ; do
case "$c1" in
"$last_name" )
printf "Last Name:%s\nFirst Name:%s\nCity:%s\nState:%s\nClass:%s\nSemester Enrolled:%s\nYear First Enrolled:%s\n\n", "$c1", "$c2", "$c3", "$c4", "$c5", "$c6", "$c7"
;;
esac
done < $2
#Case statement for ending the program
x|X) echo "The program is ending" ; exit 0;;
The error message is:
./asg7s: line 26: syntax error at line 93: `)' unexpected
Line 93 is
x|X) echo "The program is ending" ; exit 0;;
Kind of weird because I didn't mess with that part of the program so I know it has to be something in the portion I changed.
RLG
Adding something extra so I can save RLG edit without other's "approval"
This awk should do:
last_name="Gilman"
awk -F: -v name="$last_name" 'NR==1 {for(i=1;i<=NF;i++) h[i]=$i} $1==name {for(i=1;i<=NF;i++) print h[i]FS,$i}' file
Last Name: Gilman
First Name: Randy
City: Manhattan
State: KS
Class: Junior
Semester Enrolled: Spring
Year First Enrolled: 2010
It stores the first line in array h (header).
Then if it finds the pattern, print out array and data.
Here I post an alternative to a shell script, in perl:
perl -F':' -lne '
BEGIN { $name = pop; }
$. == 1 and do { #header = #F; next; };
next if m/^\s*$/;
if ( $F[0] eq $name ) {
for ( $i = 0; $i < #F; $i++ ) {
printf qq|%s: %s\n|, $header[$i], $F[$i];
}
exit 0;
}
' infile Gilman
It uses -F swith to split fields with colon, -l removes last newline character and -n opens input file and process it line by line.
The script accepts two arguments, but I extract the last one before processing assuming it's the name you want to search.
First line is saved in an array called #header and for next lines it compares first field, and in a match, prints each header followed by each field of current line and aborts the program.
It yields:
Last Name: Gilman
First Name: Randy
City: Manhattan
State: KS
Class: Junior
Semester Enrolled: Spring
Year First Enrolled: 2010

Using ARGV to get user input in awk script

I know ARGV[i] can be used for storing user input. However, I want to use it in awk script and get the ARGV[i] to compare the field from another text file. If the ARGV[i] matches the field or the field contains ARGV[i] which is the user input, then I want to return the other fields of that line.
Let say I have a file, testing.txt
123123123:Walter White:1:2:3:4
123123124:Jesse Pinkman:1:3:4:1
This is my awk script, awkScript.awk
#!/usr/bin/awk -f
BEGIN{FS = ":"; print "ENTER A NAME: "}
{
for (i = 1; i < ARGC; i++)
{
if ($2 ~ /'ARGV[i]'/)
{
print "Member ID: " $1
}
}
}
It just prints ENTER A NAME: when I execute the script file. It doesn't even get the input from the user.
From awk manual
ARGV is an array of command line arguments
That is the list of arguments passed while calling the awk sript.
you may want something like
$echo 'ENTER A NAME'
$read Name
Jesse Pinkman
$awk -v name="$Name" -F: '$2=name{print $1}' filename
123123123
123123124
Here -v option creates a variable named name in awk script and assigns the value of $Name variable from shell to name
$2=name{print $1} the $2=name selects all lines where $2 is name and prints the first column
Not sure what you're thinking about wrt using ARGV[] but here's one way to do what you want in awk:
$ awk 'BEGIN{FS=":"; msg="ENTER A NAME:"; print msg} NR==FNR{a[$2]=$0; next} $0 in a{print a[$0]} {print msg}' file -
ENTER A NAME:
Jesse Pinkman
123123124:Jesse Pinkman:1:3:4:1
ENTER A NAME:
Walter White
123123123:Walter White:1:2:3:4
ENTER A NAME:

shell script to find the number of occurence of particular word from text file

My file is ss.txt
Another instance started
Another instance started
Another instance started
Another instance started
No instance started
No instance started
No instance started
No instance started
If i use shell script program as this
#!/bin/sh
t=0
#temp=0
echo "Enter filename"
read f1
if [ -f $f1 ]
then
echo "1.count char,words,lines"
echo "2.particular word to find in file"
echo "3.exit"
echo "Enter ur choice?"
read ch
case $ch in
1) wc -c $f1
echo "characters"
wc -w $f1
echo "words"
wc -l $f1
echo "lines" ;;
2) echo "Enter the word whose occurence has to be found"
read c
t='echo $f1 | wc -c'
echo $t ;;
3) exit ;;
esac
else
echo "File does not exist"
fi
If i run this code i get the following output
i could get the option 2 correct that is word occurence is not correct
i get like
Enter filename
ss.txt
1.count char,words,lines
2.particular word in file
3.exit
Enter ur choice?
1
180 ss.txt
characters
24 ss.txt
words
8 ss.txt
lines
This i get correctly but for choice 2 i get like
Enter filename
ss.txt
1.count char,words,lines
2.particular word in file
3.exit
Enter ur choice?
2
Enter the word whose occurence has to be found
Another
0
See i get zero here but output should be 4
change from:
t='echo $f1 | wc -c'
to
t=`grep -o "$c" "${f1}"|wc -l`
Please try below commands as per your requirement
t=grep -c "text" "${f1}" ( -c, --count count of matching lines for each input file)
or
t=grep -o "text" "${f1}" ( -o,Show only the part of a matching line that matches PATTERN.)
bcsmc2rtese001 [~/Shell_p]$ grep -c "Another" ss.txt
4
bcsmc2rtese001 [~/Shell_p]$ grep -o "Another" ss.txt
Another
Another
Another
Another

counting records in unix file

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.

Resources