Unix ja shell-ohjelmointi 2001, demo 5 Unix ja shell-ohjelmointi 2001, demo 5 mallivastaukset

    1. #! /bin/sh
      wc "$@" |
      awk '{ printf "%.1f characters, %.1f words per line\n", $3/$1, $2/$1 }'
      
      Pelkästään awkillakin se sujuu, etenkin jos halutaan muuttaa sanan määritelmää, esim.
      #! /bin/awk -f
      BEGIN { FS="[^-[:alpha:]]+" }
      { c += length()+1; w += NF - (!$1) - (!$NF) }
      END { printf "%.1f characters, %.1f words per line\n", c/NR, w/NR }
      

    2. Tässä oli hieman tulkinnanvaraista mitä 'kirjain' tarkoittaa, seuraava ei laske välimerkkejä ja samaistaa isot ja pienet kirjaimet:
      #! /bin/awk -f
      { for (i=1; i<=length(); ++i) {
          c=toupper(substr($0,i,1))
          if (c ~ /[[:alpha:]]/ ) {
            n[c]++ 
            total++
          }
        }
      }
      END { for (c in n) printf "%c %.2f%%\n", c, n[c]/total*100 | "sort" }
      

    3. Huomaa että jos rivin alussa on erotin ja erotin on muu kuin välilyönti, ensimmäiseksi kentäksi tulee tyhjä, samoin viimeinen jos lopussa on erotin.
      #! /bin/sh
      FS=$1 ; shift
      awk -F"$FS" 'NF { t += NF-1 } END { print t }' "$@"
      

    4. Tässäkin 'sanan' määritelmä oli jätetty auki, seuraavassa sanassa sallitaan vain kirjaimia ja tavuviivoja:
      #! /bin/sh
      awk '
      BEGIN { FS="[^-[:alpha:]]+" }
      { for (i=0; ++i<=NF;) if ($i) { n[$i]++; total++ } }
      END {
         for (i in n)
           printf "%s %d %.3f%%\n",i,n[i],n[i]/total*100 }
      ' | sort -k2,2n | tail
      

    1. Tässä tehtävässä oli heikkoutena se että last-komennon tulostusformaatti vaihtelee eri koneissa. Päivämäärän voi kuitenkin aina löytää etsimällä viikonpäivää toisesta kentästä alkaen.
      #! /bin/sh
      last "$@" | 
      awk '
      $1 !~ /^([a-z]|$)|reboot|date/ && $2 !~ "begins" { 
        for (i=1; $++i !~ /Mon|Tue|Wed|Thu|Fri|Sat|Sun/ ;)
          ;
        date=$(i+2) " " $(i+3)
        if (olddate && date != olddate) {
           print olddate ":", total
           total=0
        }
        olddate=date
        ++total
      }
      END { print olddate ":", total }'
      

    2. #! /bin/sh
      last "$@" | 
      awk '
      $1 !~ /^[^a-z]|reboot|date/ && $2 != "begins" { 
        for (i=1; $++i !~ /Mon|Tue|Wed|Thu|Fri|Sat|Sun/ ;) 
          ;
        month=$(i+2)
        if (oldmonth && month != oldmonth) {
           print oldmonth ":",total
           total=0
        }
        oldmonth=month
        total++
      }
      END { print oldmonth ":" , total }'
      

    3. #! /bin/sh
      last "$@" | 
      awk '
      /^$/ { exit }
      $1 !~ /^(reboot|date)$/ { t[$1]++ }
      END { for (u in t) print u,t[u] }'
      

    4. Olennainen seikka tässä oli eri käyttäjien laskeminen, ts. saman käyttäjän eri logineita ei lasketa erikseen.
      #! /bin/sh
      # average number of different users logging in on each weekday
      # assuming there's at least one login per day
      last "$@" | 
      awk '
      !NF { exit }
      
      {  for (i=1; $++i !~ /Mon|Tue|Wed|Thu|Fri|Sat|Sun/ && i<=NF;) 
           ;
         date=$(i+1) $(i+2)
         if (date != olddate) {
           if (wday) {   
             for (u in users) {
      	    delete users[u] 
      	    ++daily[wday]
             }
             ++count[wday]
           }
           olddate=date
           wday=$i
         }
      }
      
      $1 !~ /^(reboot|date)$/ { users[$1]=1 }
      
      END { 
        ++count[wday]
        for (d in daily)
          printf "%s: %d total in %d dates, %.2f average\n", 
      	d, daily[d], count[d], daily[d]/count[d]
      }'
      

      Jos välipäivät (ilman yhtään loginia) halutaan laskea, tehtävä on olennaisesti vaikeampi. Seuraava versio toimii kunhan joka viikko on ainakin yksi login:

      #! /bin/sh
      # average number of different users logging in on each weekday
      # assuming there's at least one login _every week_.
      last "$@" | 
      awk '
      BEGIN {
        prevday["Mon"]="Sun"
        prevday["Tue"]="Mon"
        prevday["Wed"]="Tue"
        prevday["Thu"]="Wed"
        prevday["Fri"]="Thu"
        prevday["Sat"]="Fri"
        prevday["Sun"]="Sat"
      }
      
      ! NF { exit }
      
      {
        for (i=1; $++i !~ /Mon|Tue|Wed|Thu|Fri|Sat|Sun/ ;) 
          ;
        date=$(i+1) $(i+2)
        if (date != olddate) {
          if (wday) {
      	for (u in users) {
      	    delete users[u]
      	    ++daily[wday]
      	}
      	++count[wday]
      	while ($i != prevday[wday]) {
      	    wday=prevday[wday]
      	    count[wday]++
      	}
          }
          olddate=date
          wday=$i
        }
      }
      
      $1 !~ /^(reboot|date)$/ { users[$1]=1 }
      
      END {
        ++count[wday]
        for (d in daily)
          printf "%s: %d total in %d dates, %.2f average\n",
      	d,daily[d],count[d],daily[d]/count[d]
      }'
      
      Periaatteessa samalla logiikalla saisi käsitellyksi alle kuukauden tai alle vuodenkin loginittomat jaksot (yli 5 vuoden katkoja ei teoriassakaan enää voi aina hoitaa oikein).

    1. #!/bin/sh
      sort -t: -k3n /etc/passwd |
      awk -F: -v limit=${1:-499} '
      BEGIN { last=limit }
      {   if ($3 <= limit) next
          if ($3 > last+1) { exit }
          last=$3
      }
      END { print last+1 }'
      

      tai

      #!/bin/awk -f
      BEGIN { 
        FS=":" 
        limit=499
        if (ARGV[1]) limit=ARGV[1]
        ARGC=1
        last=limit
        while ("sort -t: -k3n /etc/passwd" | getline) {
          if ($3 <= limit) continue
          if ($3 > last+1) { exit }
          last=$3
        }
      }
      END { print last+1 }
      

    2. #! /bin/awk -f
      { for (i=0; ++i <= NF;) t[NR,i]=$i }
      END { 
        for (i=0; ++i <= NF;) {
          for (j=0; ++j <= NR;) printf " %d",t[j,i]
          printf "\n"
        }
      }
      

  1. #! /bin/awk -f
    $1 == "%DEFINE" {
      name=$2
      sub("^" $1 "[ \t]*" $2 "[ \t]*","")
      values[name]=$0
      next
    }
    { for (macro in values) gsub("%" macro "%",values[macro])
      print
    }
    

  2. #! /bin/awk -f
    BEGIN{
      if (ARGV[1] !~ /^([0-9]+\.)?([0-9]+\.)?([0-9]+)(\.[0-9]+-[0-9]+)?$/) {
        printf "invalid argument"
        exit 1
      } 
      n=split(ARGV[1],a,"[.]")
      if (split(a[n],r,"-")<2) { r[1]=25; r[2]=255; ++n }
      if (n==2) { Cnet="130.234." a[1] }
      else if (n==3) { Cnet="130." a[1] "." a[2] }
      else { Cnet=a[1] "." a[2] "." a[3] }
      ARGV[1]=""
      if (!ARGV[2]) ARGV[1]="-"
    }
    
    /^;/{ next }
    /^[a-zA-Z]/{ hostname = $1 }
    /^[ \t]/{ $0 = hostname " " $0 }
    $2 == "IN" && $3 =="A" { used[$4]=1 }
    END{
      for (c=r[1]; c<=r[2]; ++c) {
        ip=Cnet "." c
        if (!(ip in used)) print ip
      }
    }
    
    

    tai

    #! /bin/sh 
    
    case $1 in
      [!0-9]*) Cnet=130.234.164; Range=25-128;;
      *.*.*.*) Cnet=${1%.*}; Range=${1##*.} ; shift ;;
      *.*.*)   Cnet=$1; Range=25-128 ; shift;;
      *.*)     Cnet=130.234.${1%.*}; Range=${1#*.} ; shift;;
      ?*)      Cnet=130.234.$1; Range=25-128 ;shift ;;
      *)       Cnet=130.234.164; Range=25-128 ;;
    esac
    
    Start=${Range%-*}
    Stop=${Range#*-}
    
    awk -v Cnet=$Cnet '
    /^;/{ next }
    /^[a-zA-Z]/{ hostname = $1 }
    /^[ \t]/{ $0 = hostname " " $0 }
    $2 $3 == "INA" { print $4 }' "${@:-it.hosts}" >usedIPs
    
    awk 'BEGIN{ for (c='$Start'; c<='$Stop'; ++c) print "'$Cnet'." c }' |
    grep -vxf usedIPs
    

  3. #! /bin/awk -f
    $1 == "KEY" { keys[$2]=$3 }
    keys[$1] { 
    	for (i=0; ++i<=keys[$1] ;) printf "%s ",$i
            printf "\n" }
    


File translated from TEX by TTH, version 1.98.
On 21 Feb 2001, 18:53.