filtraggio un array è difficile se si considera la possibilità di elementi contenenti spazi (per non parlare nemmeno caratteri "più strano"). In particolare, le risposte fornite finora (che si riferiscono a varie forme di ${x[@]//pref*/}
) non funzioneranno con tali array.
Ho studiato un po 'questo problema e ho trovato una soluzione, ma non è una bella copertina. Ma almeno lo è.
Per esempi di esempi, supponiamo che arr
nomi l'array che vogliamo filtrare. Iniziamo con l'espressione nucleo:
for index in "${!ARR[@]}" ; do [[ …condition… ]] && unset -v 'ARR[$index]' ; done
ARR=("${ARR[@]}")
Esistono già alcuni elementi degni di nota:
"${!ARR[@]}"
restituisce indici dell'array (al contrario di elementi).
- Il modulo
"${!ARR[@]}"
è un must. Non devi saltare le quotazioni o cambiare @
a *
. Oppure l'espressione si romperà su array associativi in cui le chiavi contengono spazi (ad esempio).
- La parte successiva allo
do
può essere qualsiasi cosa tu voglia. L'idea è solo che devi fare unset
come mostrato per gli elementi che non vuoi avere nell'array.
- It is advised or even needed per utilizzare
-v
e quotazioni con unset
oppure possono accadere cose brutte.
- Se la parte successiva a
do
è come suggerito sopra, è possibile utilizzare &&
o ||
per filtrare gli elementi che passano o non superano la condizione.
- La seconda riga, riassegnazione di
ARR
, è necessaria solo con gli array non associativi e interromperà con gli array associativi. (Non sono uscito rapidamente con un'espressione generica che gestirà entrambe le cose mentre non ne ho bisogno ...). Per gli array ordinari è necessario se si desidera avere indici consecutivi.Poiché unset
su un elemento di matrice non modifica (elimina uno) elementi di indici più alti, crea solo un buco negli indici. Ora se si esegue un'iterazione solo sull'array (o lo si espande nel suo complesso), questo non crea problemi. Ma per altri casi è necessario riassegnare gli indici. Nota anche che se hai avuto qualche buco negli indici prima che venga rimosso. Pertanto, se è necessario preservare i fori esistenti, è necessario eseguire una logica maggiore a fianco della unset
e della riassegnazione finale.
Ora come si arriva alla condizione. L'espressione [[ ]]
è un modo semplice se puoi usarla. (Vedi here.) In particolare supporta la corrispondenza delle espressioni regolari usando lo Extended Regular Expressions. (Vedere here.) Prestare attenzione anche all'utilizzo di grep
o di qualsiasi altro strumento basato sulla linea per questo se si prevede che gli elementi dell'array possano contenere non solo spazi ma anche nuove linee. (Anche se un nome di file molto brutto potrebbe avere un carattere di nuova riga credo ...)
Riferendosi alla domanda stessa l'espressione [[ ]]
dovrebbe essere:
[[ ${ARR[$index]} =~ ^pref ]]
(con && unset
come sopra)
Vediamo finalmente come funziona con questi casi difficili. In primo luogo si costruisce la matrice:
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')
possiamo vedere che abbiamo tutti i casi complessi eseguendo declare -p ARR
e ottenere:
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")'
Ora corriamo l'espressione di filtro:
for index in "${!ARR[@]}" ; do [[ ${ARR[$index]} =~ ^pref ]] && unset -v 'ARR[$index]' ; done
e un altro test (declare -p ARR
) fornisce:
declare -a ARR='([1]="bar" [2]="foo" [4]="baz" [8]="
pref with new line before")'
nota come tutti gli elementi che iniziano con pref
sono stati rimossi ma gli indici non sono cambiati. Si noti inoltre che ${ARRAY[8]}
è ancora lì poiché inizia con una nuova riga anziché pref
.
Ora per la riassegnazione finale:
ARR=("${ARR[@]}")
e verificare (declare -p ARR
):
declare -a ARR='([0]="bar" [1]="foo" [2]="baz" [3]="
pref with new line before")'
che è esattamente quello che si aspettava.
Per le note di chiusura. Sarebbe bello se questo potesse essere cambiato in un unico rivestimento flessibile. Ma non penso che ci sia un modo per renderlo più breve e semplice come lo è ora senza definire funzioni o simili.
Per quanto riguarda la funzione, sarebbe anche meglio che accetti array, restituisca array e sia facile configurare il test da escludere o conservare. Ma non sono abbastanza bravo con Bash per farlo adesso.
+1 grazie per chiarire la confusione dal post di Hulk e sottolineare questo altro percorso. – kynan