Điều này có thể được thực hiện hoàn toàn bên trong bash. Mặc dù thực hiện thao tác chuỗi trong một vòng lặp trong bash rất chậm, nhưng có một thuật toán đơn giản là logarit trong số lượng các thao tác shell, vì vậy bash thuần là một lựa chọn khả thi ngay cả đối với các chuỗi dài.
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"
}
Hộp công cụ tiêu chuẩn bao gồm cmp
để so sánh các tệp nhị phân. Theo mặc định, nó chỉ ra độ lệch byte của các byte khác nhau đầu tiên. Có một trường hợp đặc biệt khi một chuỗi là tiền tố của chuỗi khác: cmp
tạo ra một thông báo khác trên STDERR; một cách dễ dàng để giải quyết vấn đề này là lấy chuỗi nào ngắn nhất.
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"
}
Lưu ý rằng cmp
hoạt động trên byte, nhưng thao tác chuỗi của bash hoạt động trên các ký tự. Điều này tạo ra sự khác biệt về các địa điểm đa bào, ví dụ như các địa điểm sử dụng bộ ký tự UTF-8. Hàm trên in tiền tố dài nhất của chuỗi byte. Để xử lý các chuỗi ký tự bằng phương thức này, trước tiên chúng ta có thể chuyển đổi các chuỗi thành mã hóa có chiều rộng cố định. Giả sử bộ ký tự của miền địa phương là một tập hợp con của Unicode, UTF-32 phù hợp với hóa đơn.
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"
}