Filtrado de una matriz es complicado si se tiene en cuenta la posibilidad de elementos que contienen espacios (por no mencionar siquiera los personajes "más raras"). En particular, las respuestas dadas hasta ahora (haciendo referencia a varias formas de ${x[@]//pref*/}
) fallarán con dichas matrices.
He investigado un poco este problema y he encontrado una solución, pero no es una buena idea. Pero al menos lo es.
Para ejemplos de ilustración supongamos arr
nombres de la matriz que queremos filtrar. Comenzaremos con la expresión núcleo:
for index in "${!ARR[@]}" ; do [[ …condition… ]] && unset -v 'ARR[$index]' ; done
ARR=("${ARR[@]}")
Ya hay algunos elementos dignos de mención:
"${!ARR[@]}"
evalúa a los índices de la matriz (en contraposición a los elementos).
- El formulario
"${!ARR[@]}"
es obligatorio. No debe saltear citas o cambiar @
a *
. O bien, la expresión se romperá en las matrices asociativas donde las claves contienen espacios (por ejemplo).
- La parte posterior a
do
puede ser lo que desee. La idea es solo que debe hacer unset
como se muestra para los elementos que no desea tener en la matriz.
- It is advised or even needed para usar
-v
y comillas con unset
o bien pueden ocurrir cosas malas.
- Si la parte posterior a
do
es como se sugirió anteriormente, puede usar &&
o ||
para filtrar los elementos que pasan o no la condición.
- La segunda línea, reasignación de
ARR
, solo se necesita con matrices no asociativas y se romperá con las matrices asociativas. (No salí rápidamente con una expresión genérica que manejará ambas cosas mientras no la necesite ...). Para matrices comunes, es necesario si desea tener índices consecutivos.Debido a que unset
en un elemento de matriz no modifica (descarta en uno) los elementos de índices más altos, simplemente hace un agujero en los índices. Ahora bien, si solo iteras sobre la matriz (o la expandes como un todo) esto no crea ningún problema. Pero para otros casos necesita reasignar índices. Tenga en cuenta también que si tiene algún agujero en los índices antes de que se elimine también. Por lo tanto, si necesita preservar los agujeros existentes, debe hacerse más lógica junto con el unset
y la reasignación final.
Ahora bien, se trata de la condición. La expresión [[ ]]
es una manera fácil si puede usarla. (Consulte here). En particular, admite la coincidencia de expresiones regulares usando Extended Regular Expressions. (Consulte here.) También tenga cuidado al usar grep
o cualquier otra herramienta basada en línea para esto si espera que los elementos de la matriz puedan contener no solo espacios sino también nuevas líneas. (Aunque un nombre de archivo muy desagradable podría tener un carácter de nueva línea que pienso ...)
En referencia a la pregunta misma la expresión [[ ]]
tendría que ser:
[[ ${ARR[$index]} =~ ^pref ]]
(con && unset
que el anterior)
Finalmente, veamos cómo funciona esto en esos casos difíciles. En primer lugar se construye la matriz:
declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces")'
ARR+=($'pref\nwith\nnew line')
ARR+=($'\npref with new line before')
podemos ver que tenemos todos los casos complejos mediante la ejecución de declare -p ARR
y conseguir:
declare -a ARR='([0]="preffoo" [1]="bar" [2]="foo" [3]="prefbaz" [4]="baz" [5]="prefbar" [6]="pref with spaces" [7]="pref
with
new line" [8]="
pref with new line before")'
Ahora corremos la expresión de filtro:
for index in "${!ARR[@]}" ; do [[ ${ARR[$index]} =~ ^pref ]] && unset -v 'ARR[$index]' ; done
y otra prueba (declare -p ARR
) da esperado:
observe cómo se eliminaron todos los elementos que comienzan con pref
pero los índices no cambiaron. Tenga en cuenta también que ${ARRAY[8]}
todavía está allí ya que comienza con una nueva línea en lugar de pref
.
Ahora para la reasignación definitiva:
ARR=("${ARR[@]}")
y comprobar (declare -p ARR
):
declare -a ARR='([0]="bar" [1]="foo" [2]="baz" [3]="
pref with new line before")'
que es exactamente lo que se esperaba.
Para las notas de cierre. Sería bueno si esto pudiera cambiarse en un trazador de líneas flexible. Pero no creo que haya una manera de hacerlo más corto y más simple como lo es ahora sin definir funciones o similares.
En cuanto a la función, sería bueno tenerla aceptar array, return array y tener una prueba fácil de configurar para excluir o mantener. Pero no soy lo suficientemente bueno con Bash para hacerlo ahora.
+1 gracias por aclarar la confusión de la publicación de Hulk y señalar este otro camino. – kynan