Um schnell prüfen zu können, ob ein Element in einer Datenbank-Tabelle enthalten ist, ohne dafür jedesmal die Datenbank abzufragen, wollte ich diese eine Tabelle, deren Inhalt ich prüfen wollte, in einem bash-Array ablegen.
Die Datenbank-Tabelle hat folgenden Inhalt (neben der ID):
AR ÄR ZZ XÉ XÉ F XE, J XÉ; P XÉ; S
Übernehmen kann man den Inhalt der mysql-Tabelle in einem bash-Script eigentlich ganz leicht:
while read line do tablearray+=("${line}") done <<(${mysqlcmd} -Bse "select name from table order by name")
Um nun auch schnell prüfen zu können, ob ein Element bereits in dem Array enthalten ist, wollte ich ungern alle Elemente durchlaufen. Stattdessen lasse ich bereits mysql die Daten sortieren. Im bash-Script selbst „suche“ ich dann über einen Binären-Suchbaum-Ansatz innerhalb des Bash-Arrays:
function contains() { declare -a haystack=("${!2}") local needle=${1} local low=0 local lowold=0 local high=${#haystack[@]} local highold=${#haystack[@]} let "pos = high / 2" while true do if [[ "${haystack[${pos}]}" == "${needle}" ]]; then # Found return 0 fi if [ "${haystack[${pos}]}" \> "${needle}" ]; then let "high = pos" let "pos = low + ( high - low ) / 2" else let "low = pos" let "pos = low + ( high - low ) / 2" fi if [ ${highold} -eq ${high} ]; then if [ ${lowold} -eq ${low} ]; then # Not found return 1 fi fi let "lowold = low" let "highold = high" done }
Aufrufen kann man diese Funktion bequem innerhalb des Scripts mit:
if contains "SearchString" tablearray[@] ; then
Nebeneffekt hierbei ist allerdings, dass lokal eine Kopie des Arrays angelegt wird. Wer das nicht möchte, muss den Teil declare -a haystack=("${!2}")
auslassen und statt $haystack
immer auf den Original-Array verweisen. Das führt auch leider dazu, dass man für jedes Array eine eigene Funktion braucht…
Bei einigen Suchen konnte das Element nicht gefunden werden (obwohl es im Array enthalten war) und – noch schlimmer – das contains() lief in einer Endlos-Schleife weiter. Nach kurzer Analyse war klar, dass die Sortierung von bash eine andere ist als das, was mysql verwendet.
Ich hatte schon vermutet, dass es wahrscheinlich an den CHARSET oder COLLATE bzw. COLLATION von mysql liegt, und habe mir daher ein bash-Script geschrieben, welches prüft, welche Kombinationen genauso sortieren wie bash. Wer Interesse hat, kann sich das Script gerne herunterladen: mysql_collate.sh
Das Skript überprüft dabei zwei Dinge gleichzeitig:
- Sind die Sonderzeichen korrekt in die Datenbank eingespeichert worden und können auch korrekt wieder ausgelesen werden?
- Ist die Sortierung von bash mit der mysql-„ORDER BY“ Klausel kompatibel?
Auf meinem System:
$ mysql --version mysql Ver 14.14 Distrib 5.5.32, for debian-linux-gnu (x86_64) using readline 6.2
Erhalte ich folgende gültige Kombinationen:
dec8 / dec8_bin cp850 / cp850_bin hp8 / hp8_english_ci hp8 / hp8_bin latin1 / latin1_bin latin2 / latin2_bin ujis / ujis_japanese_ci ujis / ujis_bin cp1250 / cp1250_bin latin5 / latin5_bin utf8 / utf8_bin utf8 / utf8_icelandic_ci ucs2 / ucs2_bin ucs2 / ucs2_icelandic_ci keybcs2 / keybcs2_bin macce / macce_bin macroman / macroman_bin cp852 / cp852_bin latin7 / latin7_bin utf8mb4 / utf8mb4_icelandic_ci utf16 / utf16_icelandic_ci cp1257 / cp1257_bin utf32 / utf32_bin utf32 / utf32_icelandic_ci eucjpms / eucjpms_japanese_ci eucjpms / eucjpms_bin
Ein wenig unangenehm ist mir das schon, aber ich arbeite nun mit einem binären Ansatz und habe für meine Zwecke latin1 / latin1_bin
ausgewählt.