Respuestas:
Realmente no necesitas tanto código:
IFS=$'\n' sorted=($(sort <<<"${array[*]}"))
unset IFS
Admite espacios en blanco en los elementos (siempre que no sea una nueva línea) y funciona en Bash 3.x.
p.ej:
$ array=("a c" b f "3 5")
$ IFS=$'\n' sorted=($(sort <<<"${array[*]}")); unset IFS
$ printf "[%s]\n" "${sorted[@]}"
[3 5]
[a c]
[b]
[f]
Nota: @sorontar ha señalado que se requiere cuidado si los elementos contienen comodines como *
o ?
:
La parte sorted = ($ (...)) está utilizando el operador "split and glob". Debe desactivar pegote:
set -f
oset -o noglob
oshopt -op noglob
o un elemento de la matriz como*
se ampliará a una lista de archivos.
El resultado es una culminación de seis cosas que suceden en este orden:
IFS=$'\n'
"${array[*]}"
<<<
sort
sorted=($(...))
unset IFS
IFS=$'\n'
Esta es una parte importante de nuestra operación que afecta el resultado de 2 y 5 de la siguiente manera:
Dado:
"${array[*]}"
se expande a cada elemento delimitado por el primer carácter de IFS
sorted=()
crea elementos dividiéndose en cada personaje de IFS
IFS=$'\n'
configura las cosas para que los elementos se expandan usando una nueva línea como delimitador, y luego se crean de manera que cada línea se convierta en un elemento. (es decir, dividir en una nueva línea).
La delimitación por una nueva línea es importante porque así es como sort
funciona (ordenando por línea). Dividir solo una nueva línea no es tan importante, pero es necesario preservar los elementos que contienen espacios o pestañas.
El valor predeterminado de IFS
es un espacio , una pestaña , seguido de una nueva línea , y no sería adecuado para nuestra operación.
sort <<<"${array[*]}"
parte<<<
, llamado aquí cadenas , toma la expansión de "${array[*]}"
, como se explicó anteriormente, y la introduce en la entrada estándar de sort
.
Con nuestro ejemplo, sort
se alimenta esta siguiente cadena:
a c
b
f
3 5
Como sort
tipo , produce:
3 5
a c
b
f
sorted=($(...))
parteLa $(...)
parte, llamada sustitución de comando , hace que su contenido ( sort <<<"${array[*]}
) se ejecute como un comando normal, mientras toma la salida estándar resultante como el literal que va a donde sea que haya estado $(...)
.
En nuestro ejemplo, esto produce algo similar a simplemente escribir:
sorted=(3 5
a c
b
f
)
sorted
luego se convierte en una matriz que se crea dividiendo este literal en cada nueva línea.
unset IFS
Esto restablece el valor de IFS
al valor predeterminado, y es solo una buena práctica.
Es para garantizar que no causamos problemas con nada de lo que dependa IFS
más adelante en nuestro script. (De lo contrario, tendríamos que recordar que hemos cambiado las cosas, algo que podría no ser práctico para scripts complejos).
IFS
, divide sus elementos en pequeños pedazos si tienen solo un tipo particular de espacio en blanco. Bueno; no perfecto :-)
unset IFS
necesario? Pensé que anteponer IFS=
a un comando definía el cambio solo a ese comando, volviendo a su valor anterior automáticamente después.
sorted=()
no es un comando sino una segunda asignación de variable.
Respuesta original:
array=(a c b "f f" 3 5)
readarray -t sorted < <(for a in "${array[@]}"; do echo "$a"; done | sort)
salida:
$ for a in "${sorted[@]}"; do echo "$a"; done
3
5
a
b
c
f f
Tenga en cuenta que esta versión hace frente a valores que contienen caracteres especiales o espacios en blanco ( excepto las nuevas líneas)
Nota readarray es compatible con bash 4+.
Editar Según la sugerencia de @Dimitre, lo actualicé a:
readarray -t sorted < <(printf '%s\0' "${array[@]}" | sort -z | xargs -0n1)
que tiene la ventaja de comprender incluso los elementos de clasificación con caracteres de nueva línea incrustados correctamente. Desafortunadamente, como lo indicó correctamente @ruakh, esto no significaba que el resultado readarray
sería correcto , ya que readarray
no tiene la opción de usar en NUL
lugar de líneas nuevas regulares como separadores de línea.
readarray -t sorted < <(printf '%s\n' "${array[@]}" | sort)
sort -z
es una mejora útil, supongo que la -z
opción es una extensión de tipo GNU.
sorted=(); while read -d $'\0' elem; do sorted[${#sorted[@]}]=$elem; done < <(printf '%s\0' "${array[@]}" | sort -z)
. Esto también funciona si está utilizando bash v3 en lugar de bash v4, porque readarray no está disponible en bash v3.
<
) combinada con la sustitución del proceso <(...)
. O para decirlo intuitivamente: porque (printf "bla")
no es un archivo.
Aquí hay una implementación de clasificación rápida de Bash pura:
#!/bin/bash
# quicksorts positional arguments
# return is in array qsort_ret
qsort() {
local pivot i smaller=() larger=()
qsort_ret=()
(($#==0)) && return 0
pivot=$1
shift
for i; do
if (( i < pivot )); then
smaller+=( "$i" )
else
larger+=( "$i" )
fi
done
qsort "${smaller[@]}"
smaller=( "${qsort_ret[@]}" )
qsort "${larger[@]}"
larger=( "${qsort_ret[@]}" )
qsort_ret=( "${smaller[@]}" "$pivot" "${larger[@]}" )
}
Usar como, por ejemplo,
$ array=(a c b f 3 5)
$ qsort "${array[@]}"
$ declare -p qsort_ret
declare -a qsort_ret='([0]="3" [1]="5" [2]="a" [3]="b" [4]="c" [5]="f")'
Esta implementación es recursiva ... así que aquí hay un resumen rápido iterativo:
#!/bin/bash
# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
qsort() {
(($#==0)) && return 0
local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
qsort_ret=("$@")
while ((${#stack[@]})); do
beg=${stack[0]}
end=${stack[1]}
stack=( "${stack[@]:2}" )
smaller=() larger=()
pivot=${qsort_ret[beg]}
for ((i=beg+1;i<=end;++i)); do
if [[ "${qsort_ret[i]}" < "$pivot" ]]; then
smaller+=( "${qsort_ret[i]}" )
else
larger+=( "${qsort_ret[i]}" )
fi
done
qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
done
}
En ambos casos, puede cambiar el orden que usa: utilicé comparaciones de cadenas, pero puede usar comparaciones aritméticas, comparar el tiempo de modificación del archivo wrt, etc. simplemente use la prueba adecuada; incluso puede hacerlo más genérico y hacer que use un primer argumento que es el uso de la función de prueba, por ejemplo,
#!/bin/bash
# quicksorts positional arguments
# return is in array qsort_ret
# Note: iterative, NOT recursive! :)
# First argument is a function name that takes two arguments and compares them
qsort() {
(($#<=1)) && return 0
local compare_fun=$1
shift
local stack=( 0 $(($#-1)) ) beg end i pivot smaller larger
qsort_ret=("$@")
while ((${#stack[@]})); do
beg=${stack[0]}
end=${stack[1]}
stack=( "${stack[@]:2}" )
smaller=() larger=()
pivot=${qsort_ret[beg]}
for ((i=beg+1;i<=end;++i)); do
if "$compare_fun" "${qsort_ret[i]}" "$pivot"; then
smaller+=( "${qsort_ret[i]}" )
else
larger+=( "${qsort_ret[i]}" )
fi
done
qsort_ret=( "${qsort_ret[@]:0:beg}" "${smaller[@]}" "$pivot" "${larger[@]}" "${qsort_ret[@]:end+1}" )
if ((${#smaller[@]}>=2)); then stack+=( "$beg" "$((beg+${#smaller[@]}-1))" ); fi
if ((${#larger[@]}>=2)); then stack+=( "$((end-${#larger[@]}+1))" "$end" ); fi
done
}
Entonces puede tener esta función de comparación:
compare_mtime() { [[ $1 -nt $2 ]]; }
y use:
$ qsort compare_mtime *
$ declare -p qsort_ret
ordenar los archivos de la carpeta actual por hora de modificación (los más nuevos primero).
NOTA. Estas funciones son puro Bash! sin utilidades externas, y sin subcapas! son seguros con cualquier símbolo divertido que pueda tener (espacios, caracteres de nueva línea, caracteres globales, etc.).
sort
ofrece es suficiente, una solución sort
+ read -a
será más rápida a partir de alrededor de, por ejemplo, 20 elementos, y cada vez más y significativamente más rápida cuanto más elementos esté tratando. Por ejemplo, en mi iMac de finales de 2012 con OSX 10.11.1 con una unidad Fusion: matriz de 100 elementos: ca. 0.03s segundos ( qsort()
) vs. ca. 0.005 segundos ( sort
+ read -a
); Matriz de 1000 elementos: ca. 0.375 segundos ( qsort()
) vs. ca. 0,014 segundos ( sort
+ read -a
).
if [ "$i" -lt "$pivot" ]; then
era necesario, de lo contrario, el "2" <"10" resuelto devuelve verdadero. Creo que esto es POSIX vs. Lexicográfico; o tal vez Enlace en línea .
Si no necesita manejar caracteres especiales de shell en los elementos de la matriz:
array=(a c b f 3 5)
sorted=($(printf '%s\n' "${array[@]}"|sort))
Con bash necesitarás un programa de clasificación externo de todos modos.
Con zsh no se necesitan programas externos y los caracteres especiales de shell se manejan fácilmente:
% array=('a a' c b f 3 5); printf '%s\n' "${(o)array[@]}"
3
5
a a
b
c
f
ksh tiene set -s
que ordenar ASCIIbetically .
set -A array x 'a a' d; set -s -- "${array[@]}"; set -A sorted "$@"
y, por supuesto, el comando set restablecerá los parámetros posicionales actuales, si los hay.
tl; dr :
Clasifique la matriz a_in
y almacene el resultado en a_out
(los elementos no deben tener nuevas líneas incrustadas [1]
):
Bash v4 +:
readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)
Bash v3:
IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)
Ventajas sobre la solución de antak :
No debe preocuparse por el bloqueo accidental (interpretación accidental de los elementos de la matriz como patrones de nombre de archivo), por lo que no se necesita ningún comando adicional para desactivar el bloqueo ( set -f
y set +f
restaurarlo más adelante).
No necesita preocuparse por restablecer IFS
con unset IFS
. [2]
Lo anterior combina el código Bash con una utilidad externa sort
para una solución que funciona con elementos arbitrarios de una sola línea y ordenación léxica o numérica (opcionalmente por campo) :
Rendimiento : para alrededor de 20 elementos o más , esto será más rápido que una solución Bash pura , de manera significativa y creciente una vez que supere los 100 elementos.
(Los umbrales exactos dependerán de su entrada, máquina y plataforma específicas).
printf '%s\n' "${a_in[@]}" | sort
realiza la clasificación (léxicamente, de forma predeterminada; consulte sort
la especificación POSIX ):
"${a_in[@]}"
se expande de forma segura a los elementos de la matriz a_in
como argumentos individuales , independientemente de lo que contengan (incluido el espacio en blanco).
printf '%s\n'
luego imprime cada argumento, es decir, cada elemento de la matriz, en su propia línea, tal cual.
Tenga en cuenta el uso de una sustitución de proceso ( <(...)
) para proporcionar la salida ordenada como entrada a read
/ readarray
(a través de la redirección a stdin, <
), porque read
/ readarray
debe ejecutarse en el shell actual (no debe ejecutarse en un subshell ) para que la variable de salida a_out
sea visible al shell actual (para que la variable permanezca definida en el resto del script).
Lectura sort
de la salida en una variable de matriz :
Bash v4 +: readarray -t a_out
lee las líneas individuales generadas por sort
los elementos de la variable de matriz a_out
, sin incluir el final \n
de cada elemento ( -t
).
Bash v3: readarray
no existe, por lo que read
debe usarse:
IFS=$'\n' read -d '' -r -a a_out
le dice read
que lea en la -a
variable array ( ) a_out
, lea toda la entrada, a través de las líneas ( -d ''
), pero dividiéndola en elementos de la matriz por nuevas líneas ( IFS=$'\n'
.$'\n'
, Que produce una nueva línea literal (LF) ), es una cadena llamada ANSI C entre comillas ).
( -r
, una opción con la que prácticamente siempre debe usarse read
, desactiva el manejo inesperado de \
caracteres).
Código de muestra anotado:
#!/usr/bin/env bash
# Define input array `a_in`:
# Note the element with embedded whitespace ('a c')and the element that looks like
# a glob ('*'), chosen to demonstrate that elements with line-internal whitespace
# and glob-like contents are correctly preserved.
a_in=( 'a c' b f 5 '*' 10 )
# Sort and store output in array `a_out`
# Saving back into `a_in` is also an option.
IFS=$'\n' read -d '' -r -a a_out < <(printf '%s\n' "${a_in[@]}" | sort)
# Bash 4.x: use the simpler `readarray -t`:
# readarray -t a_out < <(printf '%s\n' "${a_in[@]}" | sort)
# Print sorted output array, line by line:
printf '%s\n' "${a_out[@]}"
Debido al uso de sort
sin opciones, esto produce una clasificación léxica (los dígitos se ordenan antes que las letras y las secuencias de dígitos se tratan léxicamente, no como números):
*
10
5
a c
b
f
Si querías ordenar numéricamente por el primer campo, usarías en sort -k1,1n
lugar de solo sort
, lo que produce (los números no se ordenan antes que los números y los números se ordenan correctamente):
*
a c
b
f
5
10
[1] Para manejar elementos con líneas nuevas incrustadas, use la siguiente variante (Bash v4 +, con GNU sort
):
readarray -d '' -t a_out < <(printf '%s\0' "${a_in[@]}" | sort -z)
.
La útil respuesta de Michał Górny tiene una solución Bash v3.
[2] Mientras IFS
se establece en la variante Bash v3, el cambio se limita al comando .
Por el contrario, lo que sigue IFS=$'\n'
en la respuesta de antak es una asignación en lugar de un comando, en cuyo caso el IFS
cambio es global .
En el viaje en tren de 3 horas de Múnich a Frankfurt (que tuve problemas para alcanzar porque el Oktoberfest comienza mañana) estaba pensando en mi primer puesto. Emplear una matriz global es una idea mucho mejor para una función de clasificación general. La siguiente función maneja cadenas arbitrarias (líneas nuevas, espacios en blanco, etc.):
declare BSORT=()
function bubble_sort()
{ #
# @param [ARGUMENTS]...
#
# Sort all positional arguments and store them in global array BSORT.
# Without arguments sort this array. Return the number of iterations made.
#
# Bubble sorting lets the heaviest element sink to the bottom.
#
(($# > 0)) && BSORT=("$@")
local j=0 ubound=$((${#BSORT[*]} - 1))
while ((ubound > 0))
do
local i=0
while ((i < ubound))
do
if [ "${BSORT[$i]}" \> "${BSORT[$((i + 1))]}" ]
then
local t="${BSORT[$i]}"
BSORT[$i]="${BSORT[$((i + 1))]}"
BSORT[$((i + 1))]="$t"
fi
((++i))
done
((++j))
((--ubound))
done
echo $j
}
bubble_sort a c b 'z y' 3 5
echo ${BSORT[@]}
Esto imprime:
3 5 a b c z y
La misma salida se crea a partir de
BSORT=(a c b 'z y' 3 5)
bubble_sort
echo ${BSORT[@]}
Tenga en cuenta que probablemente Bash utiliza internamente punteros inteligentes, por lo que la operación de intercambio podría ser barata (aunque lo dudo). Sin embargo, bubble_sort
demuestra que funciones más avanzadas como merge_sort
también están al alcance del lenguaje shell.
local -n BSORT="$1"
al comienzo de la función. Entonces puedes correr bubble_sort myarray
para ordenar myarray .
Otra solución que usa externos sort
y hace frente a cualquier carácter especial (excepto NULs :)). Debería funcionar con bash-3.2 y GNU o BSD sort
(lamentablemente, POSIX no incluye -z
).
local e new_array=()
while IFS= read -r -d '' e; do
new_array+=( "${e}" )
done < <(printf "%s\0" "${array[@]}" | LC_ALL=C sort -z)
Primero mire la redirección de entrada al final. Estamos utilizando la función printf
integrada para escribir los elementos de la matriz, con terminación cero. La cita se asegura de que los elementos de la matriz se pasen como están, y los detalles del shell printf
hacen que reutilice la última parte de la cadena de formato para cada parámetro restante. Es decir, es equivalente a algo como:
for e in "${array[@]}"; do
printf "%s\0" "${e}"
done
La lista de elementos terminados en nulo se pasa a sort
. La -z
opción hace que lea elementos terminados en nulo, los ordene y genere también resultados terminados en nulo. Si necesita obtener solo los elementos únicos, puede pasar -u
ya que es más portátil que uniq -z
. Esto LC_ALL=C
garantiza un orden de clasificación estable independientemente de la configuración regional, a veces útil para los scripts. Si quieres elsort
respetar la configuración regional, elimínela.
La <()
construcción obtiene el descriptor para leer de la tubería generada y <
redirige la entrada estándar del while
bucle hacia ella. Si necesita acceder a la entrada estándar dentro de la tubería, puede usar otro descriptor: ejercicio para el lector :).
Ahora, de vuelta al principio. La read
salida de lectura incorporada del stdin redirigido. Establecer vacío IFS
desactiva la división de palabras, lo cual es innecesario aquí: como resultado, se read
lee toda la 'línea' de entrada a la variable proporcionada. -r
la opción deshabilita el procesamiento de escape que no es deseado aquí también Finalmente, -d ''
establece el delimitador de línea en NUL, es decir, le dice read
que lea cadenas terminadas en cero.
Como resultado, el bucle se ejecuta una vez por cada elemento de matriz terminado en cero sucesivamente, con el valor almacenado en e
. El ejemplo solo coloca los elementos en otra matriz, pero es posible que prefiera procesarlos directamente :).
Por supuesto, esa es solo una de las muchas formas de lograr el mismo objetivo. A mi entender, es más simple que implementar un algoritmo de clasificación completo en bash y, en algunos casos, será más rápido. Maneja todos los caracteres especiales, incluidas las nuevas líneas, y debería funcionar en la mayoría de los sistemas comunes. Lo más importante es que puede enseñarte algo nuevo e increíble sobre bash :).
e
y establecer un IFS vacío, use la variable REPLY.
prueba esto:
echo ${array[@]} | awk 'BEGIN{RS=" ";} {print $1}' | sort
La salida será:
3 5 5 una si C F
Problema resuelto.
Si puede calcular un número entero único para cada elemento de la matriz, así:
tab='0123456789abcdefghijklmnopqrstuvwxyz'
# build the reversed ordinal map
for ((i = 0; i < ${#tab}; i++)); do
declare -g ord_${tab:i:1}=$i
done
function sexy_int() {
local sum=0
local i ch ref
for ((i = 0; i < ${#1}; i++)); do
ch="${1:i:1}"
ref="ord_$ch"
(( sum += ${!ref} ))
done
return $sum
}
sexy_int hello
echo "hello -> $?"
sexy_int world
echo "world -> $?"
luego, puede usar estos enteros como índices de matriz, porque Bash siempre usa una matriz dispersa, por lo que no debe preocuparse por los índices no utilizados:
array=(a c b f 3 5)
for el in "${array[@]}"; do
sexy_int "$el"
sorted[$?]="$el"
done
echo "${sorted[@]}"
tipo min:
#!/bin/bash
array=(.....)
index_of_element1=0
while (( ${index_of_element1} < ${#array[@]} )); do
element_1="${array[${index_of_element1}]}"
index_of_element2=$((index_of_element1 + 1))
index_of_min=${index_of_element1}
min_element="${element_1}"
for element_2 in "${array[@]:$((index_of_element1 + 1))}"; do
min_element="`printf "%s\n%s" "${min_element}" "${element_2}" | sort | head -n+1`"
if [[ "${min_element}" == "${element_2}" ]]; then
index_of_min=${index_of_element2}
fi
let index_of_element2++
done
array[${index_of_element1}]="${min_element}"
array[${index_of_min}]="${element_1}"
let index_of_element1++
done
Hay una solución para el problema habitual de espacios y líneas nuevas:
Use un carácter que no esté en la matriz original (como $'\1'
o$'\4'
o similar).
Esta función hace el trabajo:
# Sort an Array may have spaces or newlines with a workaround (wa=$'\4')
sortarray(){ local wa=$'\4' IFS=''
if [[ $* =~ [$wa] ]]; then
echo "$0: error: array contains the workaround char" >&2
exit 1
fi
set -f; local IFS=$'\n' x nl=$'\n'
set -- $(printf '%s\n' "${@//$nl/$wa}" | sort -n)
for x
do sorted+=("${x//$wa/$nl}")
done
}
Esto ordenará la matriz:
$ array=( a b 'c d' $'e\nf' $'g\1h')
$ sortarray "${array[@]}"
$ printf '<%s>\n' "${sorted[@]}"
<a>
<b>
<c d>
<e
f>
<gh>
Esto se quejará de que la matriz fuente contiene el carácter de solución alternativa:
$ array=( a b 'c d' $'e\nf' $'g\4h')
$ sortarray "${array[@]}"
./script: error: array contains the workaround char
wa
(char alternativa) y un IFS nulo$*
.[[ $* =~ [$wa] ]]
.exit 1
set -f
IFS=$'\n'
), una variable de bucle x
y una nueva línea var ( nl=$'\n'
).$@
)."${@//$nl/$wa}"
.sort -n
.set --
.for x
sorted+=(…)
"${x//$wa/$nl}"
.Esta pregunta parece estar muy relacionada. Y por cierto, aquí hay un mergesort en Bash (sin procesos externos):
mergesort() {
local -n -r input_reference="$1"
local -n output_reference="$2"
local -r -i size="${#input_reference[@]}"
local merge previous
local -a -i runs indices
local -i index previous_idx merged_idx \
run_a_idx run_a_stop \
run_b_idx run_b_stop
output_reference=("${input_reference[@]}")
if ((size == 0)); then return; fi
previous="${output_reference[0]}"
runs=(0)
for ((index = 0;;)) do
for ((++index;; ++index)); do
if ((index >= size)); then break 2; fi
if [[ "${output_reference[index]}" < "$previous" ]]; then break; fi
previous="${output_reference[index]}"
done
previous="${output_reference[index]}"
runs+=(index)
done
runs+=(size)
while (("${#runs[@]}" > 2)); do
indices=("${!runs[@]}")
merge=("${output_reference[@]}")
for ((index = 0; index < "${#indices[@]}" - 2; index += 2)); do
merged_idx=runs[indices[index]]
run_a_idx=merged_idx
previous_idx=indices[$((index + 1))]
run_a_stop=runs[previous_idx]
run_b_idx=runs[previous_idx]
run_b_stop=runs[indices[$((index + 2))]]
unset runs[previous_idx]
while ((run_a_idx < run_a_stop && run_b_idx < run_b_stop)); do
if [[ "${merge[run_a_idx]}" < "${merge[run_b_idx]}" ]]; then
output_reference[merged_idx++]="${merge[run_a_idx++]}"
else
output_reference[merged_idx++]="${merge[run_b_idx++]}"
fi
done
while ((run_a_idx < run_a_stop)); do
output_reference[merged_idx++]="${merge[run_a_idx++]}"
done
while ((run_b_idx < run_b_stop)); do
output_reference[merged_idx++]="${merge[run_b_idx++]}"
done
done
done
}
declare -ar input=({z..a}{z..a})
declare -a output
mergesort input output
echo "${input[@]}"
echo "${output[@]}"
No estoy convencido de que necesite un programa de clasificación externo en Bash.
Aquí está mi implementación para el algoritmo simple de clasificación de burbujas.
function bubble_sort()
{ #
# Sorts all positional arguments and echoes them back.
#
# Bubble sorting lets the heaviest (longest) element sink to the bottom.
#
local array=($@) max=$(($# - 1))
while ((max > 0))
do
local i=0
while ((i < max))
do
if [ ${array[$i]} \> ${array[$((i + 1))]} ]
then
local t=${array[$i]}
array[$i]=${array[$((i + 1))]}
array[$((i + 1))]=$t
fi
((i += 1))
done
((max -= 1))
done
echo ${array[@]}
}
array=(a c b f 3 5)
echo " input: ${array[@]}"
echo "output: $(bubble_sort ${array[@]})"
Esto imprimirá:
input: a c b f 3 5
output: 3 5 a b c f
O(n^2)
. Me parece recordar que la mayoría de los algoritmos de clasificación utilizan O(n lg(n))
hasta la última docena de elementos más o menos. Para los elementos finales, se utiliza el orden de selección.
a=(e b 'c d')
shuf -e "${a[@]}" | sort >/tmp/f
mapfile -t g </tmp/f
sorted=($(echo ${array[@]} | tr " " "\n" | sort))
En el espíritu de bash / linux, canalizaría la mejor herramienta de línea de comandos para cada paso. sort
realiza el trabajo principal pero necesita una entrada separada por una nueva línea en lugar de espacio, por lo que la simple tubería de arriba simplemente hace:
Contenido de matriz de eco -> reemplazar espacio por nueva línea -> ordenar
$()
es hacer eco del resultado
($())
es poner el "resultado repetido" en una matriz
Nota : como @sorontar mencionó en un comentario a una pregunta diferente:
La parte sorted = ($ (...)) está utilizando el operador "split and glob". Debe desactivar glob: set -f o set -o noglob o shopt -op noglob o un elemento de la matriz como * se expandirá a una lista de archivos.
mapfile -t sorted < <(printf '%s\n' "${array[@]}" | sort)
contrario sorted=(); while IFS= read -r line; do sorted+=( "$line" ); done < <(printf '%s\n' | sort)
.
echo ${array[@]} | tr " " "\n"
esto se romperá si los campos de la matriz contienen espacios en blanco y caracteres globales. Además, genera una subshell y usa un comando externo inútil. Y debido a echo
ser tonto, se romperá si su matriz comienza con -e
, -E
o -n
. En lugar de utilizar: printf '%s\n' "${array[@]}"
. El otro antipatrón es: ($())
es poner el "resultado repetido" en una matriz . ¡Ciertamente no! Este es un antipatrón horrible que se rompe debido a la expansión del nombre de ruta (globalización) y la división de palabras. Nunca uses este horror.
IFS
, dividirá tus elementos en pedazos pequeños si tienen espacios en blanco. Pruebe el eg conIFS=$'\n'
omitido y vea!