views:

140

answers:

4

I need help with this shell script.

  1. Must use a loop of some sort.
  2. Must use input data exactly as shown.
  3. Output redirection should be accomplished within the script, not on the command line.

Here's the input files I have: http://pastebin.com/m3f783597

Here's what the output needs to be: http://pastebin.com/m2c53b25a

Here's my failed attempt: http://pastebin.com/m2c60b41

And that failed attempt's output: http://pastebin.com/m3460e78c

A: 

This is homework, I assume?

Read up on the sort and paste commands: man sort, man paste

tpdi
+1  A: 

Here's the help. Try to follow these as much as possible before looking at my solution below. That will help you out more in the long run, and in the short runsince it's a certainty that your educator can see this as easily as you can.

If he finds you've plagiarized code, it will probably mean an instant fail.

Your "failed attempt" as you put it is here. It's actually not too bad for a first attempt.

echo -e "Name\t\t On-Call\t\t Phone"
for daycount in 2 1 4 5 7 6 3
do
    for namecount in 3 2 6 1 7 4 5
    do
        day=`head -n $daycount p2input2|tail -n 1|cut -f 2 -d " "`
        name=`head -n $namecount p2input1|tail -n 1|cut -f 1 -d " "`
        phone=`head -n $namecount p2input1|tail -n 1|cut -f 2 -d " "`
        echo -e "$name\c"
        echo -e "\t\t$day\c"
        echo -e "\t\t$phone"
        continue
    done
done

And here's the hints:

  • You have two loops, one inside the other, each occurring 7 times. That means 49 lines of output rather than 7. You want to process each day and look up up name and phone for that day (actually name for that day and phone for that name).
  • It's not really suitable hardcoding linenumbers (although I admit it is sneaky)- what if the order of data changes? Better to search on values.
  • Tabs make things messy, use spaces instead since then the output doesn't rely on terminal settings and you don't need to worry about misaligned tabs.

And, for completeness, here's the two input files and the expected output:

p2input1                  p2input2
========                  ========
Dave 734.838.9801         Bob Tuesday
Bob 313.123.4567          Carol Monday
Carol 248.344.5576        Ted Sunday
Mary 313.449.1390         Alice Wednesday
Ted 248.496.2204          Dave Thursday
Alice 616.556.4458        Mary Saturday
Frank 634.296.3357        Frank Friday

Expected output
===============
Name            On-Call         Phone

carol           monday          248.344.5576
bob             tuesday         313.123.4567
alice           wednesday       616.556.4458
dave            thursday        734.838.9801
frank           friday          634.296.3357
mary            saturday        313.449.1390
ted             sunday          248.496.2204

Having said all that, and assuming you've gone away for at least two hours to try and get your version running, here's mine:

 1 #!/bin/bash
 2 spc20="                    "
 3 echo "Name            On-Call         Phone"
 4 echo
 5 for day in monday tuesday wednesday thursday friday saturday sunday
 6 do
 7     name=`grep -i " ${day}$" p2input2 | awk '{print $1}'`
 8     name=`echo ${name} | tr '[A-Z]' '[a-z]'`
 9     bigname=`echo "${name}${spc20}" | cut -c1-15`
10
11     bigday=`echo "${day}${spc20}" | cut -c1-15`
12
13     phone=`grep -i "^${name} " p2input1 | awk '{print $2}'`
14
15     echo "${bigname} ${bigday} ${phone}"
16 done

And the following description should help:

  • Line 1elects the right shell, not always necessary.
  • Line 2 gives us enough spaces to make formatting easier.
  • Lines 3-4 give us the title and blank line.
  • Lines 5-6 cycles through the days, one at a time.
  • Line 7 gives us a name for the day. 'grep -i " ${day}$"' searches for the given day (regardless of upper or lower case) at the end of a line in pinput2 while the awk statement gives you field 1 (the name).
  • Line 8 simply makes the name all lowercase.
  • Line 9 creates a string of the right size for output by adding 50 spaces then cutting off all at the end except for 15.
  • Line 11 does the same for the day.
  • Line 13 is very similar to line 7 except it searches pinput1, looks for the name at the start of the line and returns the phone number as the second field.
  • Line 15 just outputs the individual items.
  • Line 16 ends the loop.

So there you have it, enough hints to (hopefully) fix up your own code, and a sample as to how a professional would do it :-).

It would be wise to read up on the tools used, grep, tr, cut and awk.

paxdiablo
A: 

Pax has given a good answer, but this code invokes fewer processes (11 vs a minimum of 56 = 7 * 8). It uses an auxilliary data file to give the days of the week and their sequence number.

cat <<! >p2input3
1 Monday
2 Tuesday
3 Wednesday
4 Thursday
5 Friday
6 Saturday
7 Sunday
!

sort +1 p2input3 > p2.days
sort +1 p2input2 > p2.call
join -1 2 -2 2 p2.days p2.call | sort +2 > p2.duty
sort +0 p2input1 > p2.body
join -1 3 -2 1 p2.duty p2.body | sort +2n | tr '[A-Z]' '[a-z]' |
awk 'BEGIN { printf("%-14s %-14s %s\n", "Name", "On-Call", "Phone");
             printf "\n"; }
           { printf("%-14s %-14s %s\n", $1, $2, $4);}'
rm -f p2input3 p2.days p2.call p2.duty p2.body

The join command is powerful, but requires the data in the two files in sorted order on the joining keys. The cat command gives a list of days and the day number. The first sort places that list in alphabetic order of day name. The second sort places the names of the people on duty in alphabetic order of day name too. The first join then combines those two files on day name, and then sorts based on user name, yielding the output:

Wednesday 3 Alice
Tuesday 2 Bob
Monday 1 Carol
Thursday 4 Dave
Friday 5 Frank
Saturday 6 Mary
Sunday 7 Ted

The last sort puts the names and phone numbers into alphabetic name order. The second join then combines the name + phone number list with the name + duty list, yielding a 4 column output. This is run through tr to make the data all lower case, and then formatted with awk, which demonstrates its power and simplicity nicely here (you could use Perl or Python instead, but frankly, that would be messier).

Perl has a motto: TMTOWTDI "There's more than one way to do it".

That often applies to shell scripting too.


I suppose my code does not use a loop...oh dear. Replace the initial cat command with:

 for day in "1 Monday" "2 Tuesday" "3 Wednesday" "4 Thursday" \
            "5 Friday" "6 Saturday" "7 Sunday"
 do echo $day
 done > p2input3

This now meets the letter of the rules.

Jonathan Leffler
A: 

Try this one:

sort file1.txt > file1sort.txt
sort file2.txt > file2sort.txt
join file2sort.txt file1sort.txt | column -t > result.txt
rm file1sort.txt file2sort.txt
fgm