Preciso remover um elemento de uma matriz no shell bash. Geralmente, eu simplesmente faria:
array=("${(@)array:#<element to remove>}")
Infelizmente, o elemento que desejo remover é uma variável, então não posso usar o comando anterior. Abaixo, um exemplo:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Qualquer ideia?
zsh
.array=( ${array[@]/$delete} )
funciona como esperado no Bash. Você simplesmente perdeu o=
?Respostas:
O seguinte funciona como você gostaria em
bash
ezsh
:Se for necessário excluir mais de um elemento:
Embargo
Essa técnica, na verdade, remove prefixos correspondentes
$delete
dos elementos, não necessariamente elementos inteiros.Atualizar
Para realmente remover um item exato, você precisa percorrer a matriz, comparando o destino com cada elemento e usando
unset
para excluir uma correspondência exata.Observe que se você fizer isso e um ou mais elementos forem removidos, os índices não serão mais uma sequência contínua de inteiros.
O simples fato é que os arrays não foram projetados para serem usados como estruturas de dados mutáveis. Eles são usados principalmente para armazenar listas de itens em uma única variável sem a necessidade de desperdiçar um caractere como delimitador (por exemplo, para armazenar uma lista de strings que pode conter espaços em branco).
Se as lacunas forem um problema, você precisará reconstruir a matriz para preencher as lacunas:
fonte
$ array=(sun sunflower)
$ delete=(sun)
$ echo ${array[@]/$delete}
resulta emflower
(pluto1 pluto2 pippo)
, você terminará com(1 2 pippo)
.for element in "${array[@]}" do if [[ $element ]]; then echo ${element} fi done
Você poderia construir um novo array sem o elemento indesejado e, em seguida, atribuí-lo de volta ao array antigo. Isso funciona em
bash
:Isso produz:
fonte
Esta é a maneira mais direta de remover a definição de um valor se você souber sua posição.
fonte
echo ${array[1]}
, você obterá string nula. E para obterthree
você precisa fazerecho ${array[2]}
. Portanto,unset
não é o mecanismo certo para remover um elemento do array bash.${array[1]+x}
é uma string nula, entãoarray[1]
não está definida.unset
não altera os índices dos elementos restantes. Citar o argumento para unset não é necessário. A maneira de destruir um elemento da matriz é descrita no manual do Bash .${array[1]}
existe apenas porque o tamanho é 2. Se você quiser os índices, verifique${!array[@]}
.Aqui está uma solução de uma linha com mapfile:
Exemplo:
Este método permite grande flexibilidade modificando / trocando o comando grep e não deixa nenhuma string vazia no array.
fonte
printf '%s\n' "${array[@]}"
vez daquela coisaIFS
/ feiaecho
.-d $'\0'
funciona perfeitamente bem, enquanto apenas-d
sem o argumento não.-d $'\0'
é igual-d $'\0 something'
ou justo-d ''
.$'\0'
para maior clarezaEssa resposta é específica para o caso de exclusão de vários valores de grandes arrays, onde o desempenho é importante.
As soluções mais votadas são (1) substituição de padrão em uma matriz ou (2) iteração sobre os elementos da matriz. O primeiro é rápido, mas só pode lidar com elementos que têm prefixo distinto, o segundo tem O (n * k), n = tamanho do array, k = elementos para remover. Matrizes associativas são recursos relativamente novos e podem não ter sido comuns quando a pergunta foi postada originalmente.
Para o caso de correspondência exata, com n e k grandes, é possível melhorar o desempenho de O (n k) para O (n + k log (k)). Na prática, O (n) assumindo k muito menor que n. A maior parte da aceleração é baseada no uso de matriz associativa para identificar os itens a serem removidos.
Desempenho (tamanho da matriz n, valores k a serem excluídos). Mede o desempenho em segundos de tempo do usuário
Como esperado, a
current
solução é linear para N * K, e afast
solução é praticamente linear para K, com constante muito menor. Afast
solução é ligeiramente mais lenta em relação aocurrent
solução quando k = 1, devido à configuração adicional.A solução 'Rápida': array = lista de entrada, delete = lista de valores a serem removidos.
Comparado com a
current
solução, a partir da resposta mais votada.fonte
Aqui está uma pequena função (provavelmente muito específica do bash) envolvendo indireção da variável bash e
unset
; é uma solução geral que não envolve substituição de texto ou descartar elementos vazios e não tem problemas com citações / espaços em branco etc.Use-o
delete_ary_elmt ELEMENT ARRAYNAME
sem qualquer$
sigilo. Mude o== $word
for== $word*
para correspondências de prefixo; use${elmt,,} == ${word,,}
para correspondências que não diferenciam maiúsculas de minúsculas; etc., qualquer que seja o[[
suporte do bash .Ele funciona determinando os índices da matriz de entrada e iterando-os de trás para frente (portanto, a exclusão de elementos não atrapalha a ordem da iteração). Para obter os índices, você precisa acessar a matriz de entrada por nome, o que pode ser feito por via indireta da variável bash
x=1; varname=x; echo ${!varname} # prints "1"
.Você não pode acessar matrizes por nome
aryname=a; echo "${$aryname[@]}
, como , isso dá um erro. Você não pode fazeraryname=a; echo "${!aryname[@]}"
, isso dá a você os índices da variávelaryname
(embora não seja uma matriz). O que FUNCIONA éaryref="a[@]"; echo "${!aryref}"
, o que imprimirá os elementos do arraya
, preservando aspas de palavra de shell e espaços em branco exatamente comoecho "${a[@]}"
. Mas isso só funciona para imprimir os elementos de uma matriz, não para imprimir seu comprimento ou índices (aryref="!a[@]"
ouaryref="#a[@]"
ou"${!!aryref}"
ou"${#!aryref}"
, todos falham).Então, copio o array original por seu nome via indireta bash e obtenho os índices da cópia. Para iterar os índices ao contrário, uso um loop for estilo C. Eu também poderia fazer isso acessando os índices via
${!arycopy[@]}
e revertendo-os comtac
, que é umcat
que gira em torno da ordem da linha de entrada.Uma solução de função sem indireção variável provavelmente teria que envolver
eval
, o que pode ou não ser seguro para usar nessa situação (não sei dizer).fonte
delete_ary_elmt "d" array
e imprimir novamente o array. Você verá que o elemento errado é removido. Remover o último elemento também nunca funcionará.Para expandir as respostas acima, o seguinte pode ser usado para remover vários elementos de uma matriz, sem correspondência parcial:
Isso resultará em uma matriz contendo: (dois um dois três três quatro "um seis")
fonte
Se alguém se encontrar em uma posição em que precise se lembrar dos valores set -e ou set -x e ser capaz de restaurá-los, verifique esta essência que usa a primeira solução de exclusão de array para gerenciar sua própria pilha:
https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6
fonte
Resposta parcial apenas
Para deletar o primeiro item da matriz
Para deletar o último item da matriz
fonte
unset
.array0
no diretório atual, comoarray[0]
é glob, ele será primeiro expandido paraarray0
antes do comando unset.Usando
unset
Para remover um elemento em um índice particular, podemos usar
unset
e então copiar para outro array. Apenasunset
não é necessário neste caso. Comounset
não remove o elemento, ele apenas define a string nula para o índice particular na matriz.A saída é
Usando
:<idx>
Podemos remover alguns conjuntos de elementos usando
:<idx>
também. Por exemplo, se quisermos remover o primeiro elemento, podemos usar:1
conforme mencionado abaixo.A saída é
fonte
O script de shell POSIX não possui matrizes.
Portanto, provavelmente você está usando um dialeto específico, como
bash
korn shells ouzsh
.Portanto, sua pergunta a partir de agora não pode ser respondida.
Talvez isso funcione para você:
fonte
Na verdade, acabei de notar que a sintaxe do shell tem um comportamento embutido que permite a fácil reconstrução do array quando, como colocado na pergunta, um item deve ser removido.
Observe como construímos o array usando a
x+=()
sintaxe do bash ?Você poderia realmente adicionar mais de um item com isso, o conteúdo de todo um outro array de uma vez.
fonte
http://wiki.bash-hackers.org/syntax/pe#substring_removal
Para fazer um elemento de remoção completo, você deve executar um comando unset com uma instrução if. Se você não se preocupa em remover prefixos de outras variáveis ou em suportar espaços em branco no array, você pode simplesmente descartar as aspas e esquecer os loops for.
Veja o exemplo abaixo para algumas maneiras diferentes de limpar um array.
Resultado
Espero que ajude.
fonte
No ZSH isso é muito fácil (observe que ele usa uma sintaxe mais compatível com o bash do que o necessário, sempre que possível, para facilitar o entendimento):
Resultados:
fonte
Também existe esta sintaxe, por exemplo, se você deseja excluir o segundo elemento:
que é na verdade a concatenação de 2 guias. A primeira do índice 0 ao índice 1 (exclusivo) e a segunda do índice 2 ao final.
fonte
O que eu faço é:
BAM, esse item foi removido.
fonte
array=('first item' 'second item')
.Esta é uma solução rápida e suja que funcionará em casos simples, mas será interrompida se (a) houver caracteres especiais regex
$delete
ou (b) houver qualquer espaço em qualquer item. Começando com:Exclua todas as entradas que correspondam exatamente a
$delete
:resultando em
echo $array
-> pippo e certificando-se de que é um array:echo $array[1]
-> pippofmt
é um pouco obscuro:fmt -1
quebra na primeira coluna (para colocar cada item em sua própria linha. É aí que surge o problema com itens em espaços.)fmt -999999
quebra de volta em uma linha, recolocando os espaços entre os itens. Existem outras maneiras de fazer isso, comoxargs
.Adendo: se você deseja excluir apenas a primeira correspondência, use sed, conforme descrito aqui :
fonte
Que tal algo como:
fonte
Para evitar conflitos com índice de matriz usando
unset
- ver https://stackoverflow.com/a/49626928/3223785 e https://stackoverflow.com/a/47798640/3223785 para mais informações - transferir a matriz para si:ARRAY_VAR=(${ARRAY_VAR[@]})
.[Ref .: https://tecadmin.net/working-with-array-bash-script/ ]
fonte
fonte