Questo può essere fatto interamente all'interno di bash. Sebbene la manipolazione delle stringhe in un ciclo in bash sia lenta, esiste un semplice algoritmo logaritmico nel numero di operazioni della shell, quindi bash puro è un'opzione praticabile anche per stringhe lunghe.
longest_common_prefix () {
local prefix= n
## Truncate the two strings to the minimum of their lengths
if [[ ${#1} -gt ${#2} ]]; then
set -- "${1:0:${#2}}" "$2"
else
set -- "$1" "${2:0:${#1}}"
fi
## Binary search for the first differing character, accumulating the common prefix
while [[ ${#1} -gt 1 ]]; do
n=$(((${#1}+1)/2))
if [[ ${1:0:$n} == ${2:0:$n} ]]; then
prefix=$prefix${1:0:$n}
set -- "${1:$n}" "${2:$n}"
else
set -- "${1:0:$n}" "${2:0:$n}"
fi
done
## Add the one remaining character, if common
if [[ $1 = $2 ]]; then prefix=$prefix$1; fi
printf %s "$prefix"
}
La cassetta degli attrezzi standard include cmp
per confrontare i file binari. Per impostazione predefinita, indica l'offset di byte dei primi byte diversi. C'è un caso speciale quando una stringa è un prefisso dell'altra: cmp
produce un messaggio diverso su STDERR; un modo semplice per affrontarlo è quello di prendere qualunque stringa sia la più corta.
longest_common_prefix () {
local LC_ALL=C offset prefix
offset=$(export LC_ALL; cmp <(printf %s "$1") <(printf %s "$2") 2>/dev/null)
if [[ -n $offset ]]; then
offset=${offset%,*}; offset=${offset##* }
prefix=${1:0:$((offset-1))}
else
if [[ ${#1} -lt ${#2} ]]; then
prefix=$1
else
prefix=$2
fi
fi
printf %s "$prefix"
}
Si noti che cmp
opera su byte, ma la manipolazione di stringhe di bash opera su caratteri. Ciò fa la differenza nelle impostazioni locali multibyte, ad esempio impostazioni locali che utilizzano il set di caratteri UTF-8. La funzione sopra stampa il prefisso più lungo di una stringa di byte. Per gestire le stringhe di caratteri con questo metodo, possiamo prima convertire le stringhe in una codifica a larghezza fissa. Supponendo che il set di caratteri della locale sia un sottoinsieme di Unicode, UTF-32 si adatta al conto.
longest_common_prefix () {
local offset prefix LC_CTYPE="${LC_ALL:=$LC_CTYPE}"
offset=$(unset LC_ALL; LC_MESSAGES=C cmp <(printf %s "$1" | iconv -t UTF-32) \
<(printf %s "$2" | iconv -t UTF-32) 2>/dev/null)
if [[ -n $offset ]]; then
offset=${offset%,*}; offset=${offset##* }
prefix=${1:0:$((offset/4-1))}
else
if [[ ${#1} -lt ${#2} ]]; then
prefix=$1
else
prefix=$2
fi
fi
printf %s "$prefix"
}