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 '|'