bash: preenchendo o array com elementos contendo espaços e caracteres especiais

4

Eu quero ser capaz de preencher elementos de um array com strings arbitrárias, ou seja, strings que podem conter \ e espaços por exemplo. Eu escrevi isso:

#!/bin/bash
function populate_array () {
    if [ "$#" -gt 0 ] ; then
        # Enter array w/ elements as argument of executable 
        array=($@)
        n=$#
    else
        # Invoke executable with no arg,, enter array element later
        read -p "Enter array elements separated by spaces: " -a array  
        n=${#array[@]} 
    fi
    printf "%d array elements \n" "$n"
} 

populate_array "$@"

while (("$n" > 0))   # while [ "$n" -gt 0 ] ALSO WORKS
do
    printf "%s \n" "${array[$n-1]}"
    n=$n-1
done
exit 0

O bloco while serve apenas para verificar os elementos da matriz. A função é simples o suficiente para funcionar bem para argumentos que não contenham space ou \ . Não o contrário.

Tentando inserir argumentos no executável como:

#!> bash [scriptname] lkl1239 343.4l 3,344 (34) "lklk  lkl" lkaa\ lkc

Gostaria de ver 6 argumentos:

lkl1239
343.4l 
3,344
(34)
lklk  lkl
lkaa lkc

Em vez disso, sou jogado:

  • Para ( = > bash: erro de sintaxe próximo do token inesperado '34'
  • As seqüências contendo espaço são interpretadas como x + 1, onde x é o número de espaços não consecutivos nem no início nem em o fim de uma string.
  • Bash ignora o que vem depois da primeira ocorrência de \

Como isso é feito?

    
por Cbhihe 18.06.2015 / 15:55

4 respostas

3

O que você está fazendo é complicado. A maneira normal é evitar isso e apenas passar os valores da matriz como argumentos. Para ter as duas opções, você teria que usar eval :

#!/bin/bash
function populate_array () {
    if [ "$#" -gt 0 ] ; then
        # Enter array w/ elements as argument of executable 
        # Note the quotes, they are needed
        array=("$@");
        n=$#
    else
        # Invoke executable with no arg, enter array element later
        # Read a string instead of an array and use eval to make it
        # into an array. That way, you can use tricks like escaping
        # spaces. You also need the -r option to protect the backslashes
        # so that eval will see them. 
        read -r  -p "Enter array elements separated by spaces: " string
        eval array="( $(printf '%s\n' "$string") )"
        n=${#array[@]} 
    fi
    printf "%d array elements \n" "$n"
} 

populate_array "$@"

while (("$n" > 0))   # while [ "$n" -gt 0 ] ALSO WORKS
do
    printf "%s \n" "${array[$n-1]}"
    n=$n-1
done
exit 0

Você ainda precisa escapar dos parênteses se você passar os valores da matriz como um argumento, pois ( ) são caracteres reservados para o bash. Com essa ressalva, o script acima deve funcionar conforme o esperado:

$ foo.sh lkl1239 343.4l 3,344 \(34\) "lklk  lkl" lkaa\ lkc
6 array elements 
lkaa lkc 
lklk  lkl 
(34) 
3,344 
343.4l 
lkl1239 

e

$ foo.sh
Enter array elements separated by spaces:  lkl1239 343.4l 3,344 \(34\) "lklk  lkl" lkaa\ lkc 
6 array elements 
lkaa lkc 
lklk  lkl 
(34) 
3,344 
343.4l 
lkl1239 
    
por terdon 18.06.2015 / 17:37
1

Basta citar o $@ como você já fez corretamente na invocação da função:

array=("$@")

Como man bash coloca:

% bl0ck_qu0te%     
por choroba 18.06.2015 / 16:18
0

Caracteres especiais geralmente precisam ser escapados com barra invertida, assim:

 $ array-script.sh   lkl1239 343.4l 3,344 \(34\) "lklk  lkl" lkaa\ lkc                                                                  
6 array elements 
lkaa lkc 
lklk  lkl 
(34) 
3,344 
343.4l 
lkl1239 

Os colchetes são tratados pelo shell como metacaracteres, portanto, precisam ser escapados. A barra invertida em lkaa \ lkc já escapa do espaço entre lkaa e lkc, então essas duas strings são tratadas como uma só. O mesmo que com cd /home/user/bin/NAME\ WITH\ SPACE

E de man bash :

   metacharacter
                  A character that, when unquoted, separates words.  One of the following:
                  |  & ; ( ) < > space tab
    
por Sergiy Kolodyazhnyy 18.06.2015 / 16:30
0

você pode tentar colocando abaixo no topo do script

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

e

IFS=$SAVEIFS 

na parte inferior do script

    
por vijayant 15.06.2016 / 14:46