Vaikka tehtävät olikin ajateltu tehtäviksi ilman awkia, seuraavassa on joistakin myös awk-ratkaisu (vaikkapa vihjeeksi seuraaviin...).
Seuraavat ratkaisut olettavat nimien olevan normaaleja, ts. ei mitään erikoismerkkejä tms.
Jos tyydytään etsimään mitä tahansa sukunimen edessä olevaa pienellä kirjaimella alkavaa sanaa päästään helpommalla:
grep -E ' [[:lower:]]+ [^ ]+ *$' "$@"
tai
awk '$(NF-1) ~ /^[[:lower:]]/' "$@"
Rivinvaihtojen muuttaminen on helpointa tr:llä, pitää vain muistaa poistaa viimeinen lopusta:
pat=$(tr '\n' '|' <etunimet.txt) grep -E "((${pat%|}) +)+[^ ]+ *$" "$@"Sedillä se käy hieman hankalammin mutta ilman apumuuttujia joten se voidaan tehdä komentokorvauksella:
grep -E "(($( sed -n -e :a -e N -e '$!ba' -e '$s/\n/|/gp' etunimet.txt )) +)+[^ ]+ *$" "$@"Samoin awkilla:
grep -E "(($( awk 'NR>1 { print "|" } { print }' ORS='' etunimet.txt )) +)+[^ ]+ *$" "$@"
Nuo saattavat kaatua komentorivin maksimipituuteen jos nimiä on paljon, joten voi olla parempi käyttää aputiedostoa:
sed -n -e :a -e N -e '$!ba' -e '$s/\n/|/gp' etunimet.txt >nimimalli grep -Ef nimimalli "$@"
tr a-zA-Z b-zaB-ZA tr a-zA-Z za-yZA-Y
sed 's/\([[:alpha:]]\{1,\}\)\([^[:alpha:]]\{1,\}\)'\ '\([[:alpha:]]\{1,\}\)/\3\2\1/g' "$@"
grep -vEix "[a-f0-9]{12} ([a-f0-9][a-f0-9]:){5}[a-f0-9]{2} [a-f0-9]{6}-[a-f0-9]{6}" "$@"
#! /bin/sed -f /^.\{6\}+/w 18XX /^.\{6\}-/w 19XX /^.\{6\}A/w 20XX
#! /bin/sh sed 's/>/>\ /g s/</\ </g' "$@" | grep -i '<a href'
#! /bin/sed -f /<[Aa] *[Hh][Rr][Ee][Ff]/!d s+\(<[Aa] *[Hh][Rr][Ee][Ff]\)+\ \1$+ s+.*\n++ :check /<\/[Aa]>/!{ N bcheck } s+\(.\)\(<[Aa]\)+\1¤\2+ s+\(</[Aa]>\)[^¤]*+\1+ s/\n/ /g s/¤/\ / P D
sed "$(sed 's+\(.\)[ ]*\(.*\)+s/\1/\\\2/g+' htmlspecials)" "$@" sed "$(sed 's+\(.\)[ ]*\(.*\)+s/\2/\1/g+' htmlspecials)" "$@"tai awkilla:
sed "$(awk '{print "s/" $1 "/\\" $2 "/"}' htmlspecials)" "$@" sed "$(awk '{print "s/" $2 "/" $1 "/"}' htmlspecials)" "$@"
#! /bin/sh while [ -s "$file" ] ;do sed -n ' /^$/,${ /^From /,${ w restfile d } } w firstfile' "$file" if sed '/^$/q' firstfile | grep -q "^Subject:.*kalakukko" ;then cat firstfile >>$HOME/censored else sed '1a\ X-note: approved by censorship board' firstfile fi rm firstfile mv restfile "$file" done
Rakennetaan yksi regexp joka vastaa juuri halutunkokoisia palindromeja, jokaiselle pituudelle oma malli ja ne omille riveilleen:
#! /bin/sh # brute-force palindrome finder case "${1}" in *-*) BEGIN=${1%-*}; END=${1#*-} ;; ?*) BEGIN=${1}; END=$BEGIN ;; *) BEGIN=2; END=19 ;; esac PAT= i=$BEGIN while [ $i -le $END ] ;do case "$i" in 2) PAT="$PAT \([[:alpha:]]\)\1" ;; 3) PAT="$PAT \([[:alpha:]]\)[[:alpha:]]\1" ;; 4) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\2\1" ;; 5) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)[[:alpha:]]\2\1" ;; 6) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\3\2\1" ;; 7) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)[[:alpha:]]\3\2\1" ;; 8) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\4\3\2\1" ;; 9) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)[[:alpha:]]"\ "\4\3\2\1" ;; 10) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\5\4\3\2\1" ;; 11) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "[[:alpha:]]\5\4\3\2\1" ;; 12) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)\6\5\4\3\2\1" ;; 13) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)[[:alpha:]]\6\5\4\3\2\1" ;; 14) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)\([[:alpha:]]\)\7\6\5\4\3\2\1" ;; 15) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)\([[:alpha:]]\)[[:alpha:]]\7\6\5\4\3\2\1" ;; 16) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\8\7\6\5\4\3\2\1" ;; 17) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)[[:alpha:]]\8\7\6\5\4\3\2\1" ;; 18) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\9\8\7\6\5\4\3\2\1" ;; 19) PAT="$PAT \([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)"\ "\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)\([[:alpha:]]\)[[:alpha:]]"\ "\9\8\7\6\5\4\3\2\1" ;; esac i=$((i+1)) done PAT="${PAT%?}" tr -cs '[:alpha:]' '[\n*]' | grep -x "$PAT"
Seuraavassa on sama idea mutta mallin osat rakennetaan dynaamisesti:
#! /bin/sh case "${1}" in *-*) BEGIN=${1%-*}; END=${1#*-} ;; ?*) BEGIN=${1}; END=$BEGIN ;; *) BEGIN=2; END=19 ;; esac PAT= i=$BEGIN while [ $i -le $END ] ;do mid=$((i/2)) j=1 while [ $j -le $mid ] ;do PAT="$PAT\\([[:alpha:]]\\)" j=$((j+1)) done [ $((i%2)) = 1 ] && PAT="$PAT[[:alpha:]]" j=$mid while [ $j -ge 1 ] ;do PAT="$PAT\\$j" j=$((j-1)) done PAT="$PAT " i=$((i+1)) done tr -cs '[:alpha:]' '[\n*]' | grep -x "${PAT%?}"Kumpikaan edellisistä ei muuten toimi Gnu grep 2.4.2:lla (siinä on bugi, mutta kuulemma ''it will be fixed in the next release'').
Seuraava valitsee ensin yhdellä grepillä halutun mittaiset sanat ja sitten toisella niistä palindromit pituuteen katsomatta (enintään 19-kirjaimiset):
# /bin/sh case "${1}" in *-*) BEGIN=${1%-*}; END=${1#*-} ;; ?*) BEGIN=${1}; END=$BEGIN ;; *) BEGIN=2; END=19 ;; esac tr -cs '[:alpha:]' '[\n*]' | grep -x '.\{'$BEGIN','$END'\}' | grep -x '\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)'\ '\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)\(.\)'\ '.\{0,1\}\9\8\7\6\5\4\3\2\1'
Sama idea voidaan toteuttaa yhdellä sed-kutsulla:
# /bin/sh case "${1}" in *-*) BEGIN=${1%-*}; END=${1#*-} ;; ?*) BEGIN=${1}; END=$BEGIN ;; *) BEGIN=2; END=19 ;; esac tr -cs '[:alpha:]' '[\n*]' | sed '/^.\{'$BEGIN','$END'\}$/!d /^\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)'\ '\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)\(.\{0,1\}\)\(.\)'\ '.\{0,1\}\9\8\7\6\5\4\3\2\1$/!d'
Helpompaa ehkä on testata jokainen sana kirjain kerrallaan, mikä lisäksi toimii pitemmillekin, mutta sh:lla tehtynä tämä on paljon hitaampaa:
#! /bin/sh # palindrome finder case "${1}" in *-*) BEGIN=${1%-*}; END=${1#*-} ;; ?*) BEGIN=${1}; END=$BEGIN ;; *) BEGIN=2; END=19 ;; esac tr -cs '[:alpha:]' '[\n*]' | while read word ;do if [ ${#word} -lt $BEGIN ] || [ ${#word} -gt $END ] ;then continue fi tmp=$word while [ ${#tmp} -gt 1 ] ;do [ ${tmp#${tmp%?}} = ${tmp%${tmp#?}} ] || continue 2 tmp=${tmp%?} tmp=${tmp#?} done echo $word done
Awkia käyttäen sama käy helpommin ja nopeammin:
#! /bin/sh # palindrome finder case "${1}" in *-*) BEGIN=${1%-*}; END=${1#*-} ;; ?*) BEGIN=${1}; END=$BEGIN ;; *) BEGIN=2; END=19 ;; esac tr -cs '[:alpha:]' '[\n*]' | awk -v min=$BEGIN -v max=$END ' length() >= min && length() <= max { for (i=int(length()/2); i; --i) if (substr($0,i,1) != substr($0,length()-i+1,1)) next print }'
Awkilla voi hoitaa myös sanojen erottelun ja argumenttien käsittelyn:
#! /bin/awk -f # palindrome finder BEGIN{ split(ARGV[1],a,"-") if (!a[1]) a[1]=2 if (!a[2]) a[2]=19 FS="[^[:alpha:]]+" ARGC=1 } { for (n=0; ++n<=NF;) { len=length($n) if (len<a[1] || len>a[2]) continue p=1 for (i=int(len/2); p && i; --i) p = (p && (substr($n,i,1) == substr($n,len-i+1,1))) if (p) print $n } }
Mainittakoon vielä että yhdessä koneessa noiden suoritusajat megatavun tiedostolle olivat järjestyksessä noin 4s, 4s, 20s, 20s, 60s, 6s ja 8s. Luettavuuden ja tehokkuuden kompromissina sh-tr-awk -yhdistelmä lienee paras.
#! /bin/sh while read pal ;do first=$(printf "%s\n" "$pal" | cut -c1-$((${#pal}/2))) printf "%s|%s\n" "$first" "${pal#$first}" done | sort -t'|' -k2 | tr -d '|'
Tämäkin kävisi awkilla helpommin:
#! /bin/sh awk '{ l=int(length()/2); print substr($0,1,l)"|"substr($0,l+1) }' | sort -t'|' -k2 | tr -d '|'