Lendo uma string delimitada em uma matriz no Bash

206

Eu tenho uma variável que contém uma seqüência de caracteres delimitada por espaço:

line="1 1.50 string"

Quero dividir essa seqüência de caracteres com espaço como delimitador e armazenar o resultado em uma matriz, para que o seguinte:

echo ${arr[0]}
echo ${arr[1]}
echo ${arr[2]}

saídas

1
1.50
string

Em algum lugar, encontrei uma solução que não funciona:

arr=$(echo ${line})

Se eu executar as instruções de eco acima depois disso, recebo:

1 1.50 string
[empty line]
[empty line]

Eu também tentei

IFS=" "
arr=$(echo ${line})

com o mesmo resultado. Alguém pode ajudar por favor?

Nikola Novak
fonte
Veja esta resposta no Unix e Linux Stack Exchange: Usando sed com herestring (<<<) e leia -a . set -f; arr=($string); set +fparece ser mais rápido que read -r -a <<< $string.
codeforester

Respostas:

331

Para converter uma string em uma matriz, use

arr=($line)

ou

read -a arr <<< $line

É crucial não usar aspas, pois isso funciona.

kev
fonte
7
e para fazer uma verificação de sanidade de sua bela nova matriz:for i in ${arr[@]}; do echo $i; done
Banjer
5
ou apenasecho ${arr[@]}
Banjer
13
Ambas as formas podem falhar se $linehouver caracteres brilhantes. mkdir x && cd x && touch A B C && line="*" arr=($line); echo ${#arr[@]}dá 3
Tino
3
declare -a "arr=($line)"irá ignorar IFSdelimitadores dentro de strings entre aspas
Dave
4
@Tino No. When line='*' , read -a arr <<<$linesempre funciona, mas apenas arr=($line)falha.
Johnny Wong
39

Tente o seguinte:

arr=(`echo ${line}`);
Randy
fonte
5
Agradável - Essa solução também funciona no shell Z, onde algumas das outras abordagens acima falham.
precisa saber é o seguinte
Seu trabalho, você poderia explicar por que ele funciona?
precisa saber é o seguinte
2
Observação: isso também não funciona quando a linha tem '*', comoline='*'
Johnny Wong
34

In: arr=( $line ). A "divisão" vem associada a "glob".
Os caracteres universais ( *, ?e[] ) serão expandidos para nomes de arquivos correspondentes.

A solução correta é apenas um pouco mais complexa:

IFS=' ' read -a arr <<< "$line"

Nenhum problema de globbing; o caractere de divisão é definido $IFS, variáveis ​​citadas.

Isaac
fonte
5
Essa deve ser a resposta aceita. A afirmação arr=($line)na resposta aceita sofre de problemas de globbing. Por exemplo, tente: line="twinkling *"; arr=($line); declare -p arr.
codeforester
A citação é opcional para herestring, <<<mas pode ser uma boa ideia ainda usar aspas duplas para garantir consistência e legibilidade.
codeforester
7

Se você precisar de expansão de parâmetro, tente:

eval "arr=($line)"

Por exemplo, pegue o seguinte código.

line='a b "c d" "*" *'
eval "arr=($line)"
for s in "${arr[@]}"; do 
    echo "$s"
done

Se o diretório atual contendo os arquivos a.txt, b.txte c.txt, em seguida, executar o código deve produzir a seguinte saída.

a
b
c d
*
a.txt
b.txt
c.txt
David Anderson
fonte
-9
line="1 1.50 string"

arr=$( $line | tr " " "\n")

for x in $arr
do
echo "> [$x]"
done
Hitesh
fonte
O looping é errado, ele divide a matriz fina e o tubo na tré supérfluo, mas ele deve varrer "${arr[@]}"vez, não$arr
Zorf