Um script pega um URL, analisa-o em busca dos campos obrigatórios e redireciona sua saída para ser salva em um arquivo, file.txt . A saída é salva em uma nova linha cada vez que um campo é encontrado.
arquivo.txt
A Cat
A Dog
A Mouse
etc...
Quero pegar file.txt
e criar um array a partir dele em um novo script, onde cada linha passa a ser sua própria variável de string no array. Até agora tentei:
#!/bin/bash
filename=file.txt
declare -a myArray
myArray=(`cat "$filename"`)
for (( i = 0 ; i < 9 ; i++))
do
echo "Element [$i]: ${myArray[$i]}"
done
Quando executo este script, o espaço em branco resulta em palavras divididas e em vez de
Saída desejada
Element [0]: A Cat
Element [1]: A Dog
etc...
Eu acabo recebendo isso:
Saída real
Element [0]: A
Element [1]: Cat
Element [2]: A
Element [3]: Dog
etc...
Como posso ajustar o loop abaixo de modo que toda a string em cada linha corresponda um a um com cada variável na matriz?
Respostas:
Use o
mapfile
comando:mapfile -t myArray < file.txt
O erro está usando
for
- a maneira idiomática de repetir as linhas de um arquivo é:while IFS= read -r line; do echo ">>$line<<"; done < file.txt
Veja BashFAQ / 005 para mais detalhes.
fonte
while IFS= read -r; do lines+=("$REPLY"); done <file
.mapfile
não será executado como esperado em muitas máquinas sem etapas adicionais. Macs @ericslaw continuarão a ser fornecidos com o bash 3.2.57 em um futuro próximo. Versões mais recentes usam uma licença que exigiria que a apple compartilhasse ou permitisse coisas que ela não deseja compartilhar ou permitir.mapfile
ereadarray
(que são sinônimos) estão disponíveis no Bash versão 4 e superior. Se você tiver uma versão mais antiga do Bash, poderá usar um loop para ler o arquivo em uma matriz:arr=() while IFS= read -r line; do arr+=("$line") done < file
Caso o arquivo tenha uma última linha incompleta (falta de nova linha), você pode usar esta alternativa:
arr=() while IFS= read -r line || [[ "$line" ]]; do arr+=("$line") done < file
Relacionado:
fonte
IFS= read -r line || [[ "$line" ]]
para que funcione. Caso contrário, funciona muito bem!do
?Você também pode fazer isso:
oldIFS="$IFS" IFS=$'\n' arr=($(<file)) IFS="$oldIFS" echo "${arr[1]}" # It will print `A Dog`.
Nota:
A expansão do nome do arquivo ainda ocorre. Por exemplo, se houver uma linha com um literal,
*
ele se expandirá para todos os arquivos na pasta atual. Portanto, use-o apenas se o seu arquivo estiver livre desse tipo de cenário.fonte
IFS
apenas temporariamente (para que ele recupere seu valor original após este comando), enquanto ainda persiste a atribuição paraarr
?IFS=$'\n' arr=($(echo 'a 1'; echo '*'; echo 'b 2')); printf "%s\n" "${arr[@]}"
IFS=... command
não mudaIFS
no shell atual. No entanto,IFS=... other_variable=...
(sem nenhum comando) muda ambosIFS
eother_variable
no shell atual.arr=
notação (em comparação commapfile
/readarray
).Você pode simplesmente ler cada linha do arquivo e atribuí-la a um array.
#!/bin/bash i=0 while read line do arr[$i]="$line" i=$((i+1)) done < file.txt
fonte
Use mapfile ou leia -a
Sempre verifique seu código usando shellcheck . Freqüentemente, você receberá a resposta correta. Neste caso, SC2207 cobre a leitura de um arquivo que tem valores separados por espaço ou separados por nova linha em uma matriz.
Não faça isso
Arquivos com valores separados por novas linhas
mapfile -t array < <(mycommand)
Arquivos com valores separados por espaços
IFS=" " read -r -a array <<< "$(mycommand)"
A página shellcheck fornecerá a razão de por que isso é considerado a prática recomendada.
fonte
Esta resposta diz para usar
mapfile -t myArray < file.txt
Eu fiz um shim para
mapfile
se você quiser usarmapfile
no bash <4.x por qualquer motivo. Ele usa o existentemapfile
comando se você estiver em bash> = 4.xAtualmente, apenas opções
-d
e-t
funcionam. Mas isso deve ser suficiente para o comando acima. Eu só testei no macOS. No macOS Sierra 10.12.6, o bash do sistema é3.2.57(1)-release
. Portanto, o calço pode ser útil. Você também pode simplesmente atualizar seu bash com homebrew, construir o bash você mesmo, etc.Ele usa essa técnica para definir variáveis em uma pilha de chamadas.
fonte