(Bash) Editando o conteúdo do array a partir da função

4

Estou tentando preencher todos os itens de uma matriz com 20 caracteres com espaço em branco, mas não consigo fazer com que meu loop funcione corretamente. Parece incrementar corretamente os itens da matriz, mas não altera os itens. Para onde estou indo errado?

#!/bin/bash

testArray=( "bish" "bash" "bosh")

padLine () {
array=( "${@}" )
testLength=20
counter=0

##loop begins here##
for i in "${array[@]}";
do
size=${#array[$counter]}
testLength=20

#echo ""
#echo "size: " $size
#echo "Tlength: " $testLength
#echo "count: " ${array[$counter]}
#echo ""

if [ $size -lt $testLength ]
then 
    offset=$( expr $testLength - $size )

    #echo "Offset: " $offset

    case $offset in
        0)
            l0=""
            ;;
        1)
            l1=" "
            array[$counter]=${array[$counter]/%/$l1};;
        2)
            l2="  "
            array[$counter]="${array[$counter]/%/$l2}";;
        3)
            l3="   "
            array[$counter]=${array[$counter]/%/$l3};;
        4)
            l4="    "
            array[$counter]="${array[$counter]/%/$l4}";;
        5)
            l5="     "
            array[$counter]="${array[$counter]/%/$l5}";;
        6)
            l6="      "
            array[$counter]=${array[$counter]/%/$l6};;
        7)
            l7="       "
            array[$counter]=${array[$counter]/%/$l7};;
        8)
            l8="        "
            array[$counter]=${array[$counter]/%/$l8};;
        9)
            l9="         "
            array[$counter]=${array[$counter]/%/$l9};;
        10)
            l10="          "
            array[$counter]=${array[$counter]/%/$l10};;
        11)
            l11="           "
            array[$counter]=${array[$counter]/%/$l11};;
        12)
            l12="            "
            array[$counter]=${array[$counter]/%/$l12};;
        13)
            l13="             "
            array[$counter]=${array[$counter]/%/$l13};;
        14)
            l14="              "
            array[$counter]=${array[$counter]/%/$l14};;
        15)
            l15="               "
            array[$counter]=${array[$counter]/%/$l15};;
        16)
            l16="                "
            array[$counter]=${array[$counter]/%/$l16};;
        17)
            l17="                 "
            array[$counter]=${array[$counter]/%/$l17};;
        18)
            l18="                  "
            array[$counter]=${array[$counter]/%/$l18};;
        19)
            l19="                   "
            array[$counter]=${array[$counter]/%/$l19};;

        *)
    esac
fi
counter=$( expr $counter + 1 )  
done
}

padLine "${testArray[@]}"


echo -e "${testArray[0]}" 
echo -e "${testArray[1]}"
echo -e "${testArray[2]}"

Resultado esperado:

bish                #lines end here, padded to 20 chars
bash                #                
bosh                #

Saída real:

bish# no padding
bash
bosh
    
por user19923311885 13.06.2018 / 14:26

6 respostas

6

Apenas para saída:

array=( bish bash bosh )
printf '%-20s#\n' "${array[@]}"

Isso produziria

bish                #
bash                #
bosh                #

... onde # ocorre na coluna 21.

Para criar uma nova matriz (e imprimi-la):

array=( bish bash bosh )

for elem in "${array[@]}"; do
    padarr+=( "$( printf '%-20s#' "$elem" )" )
done

printf '%s\n' "${padarr[@]}"

Com /bin/sh , apenas imprimindo:

set -- bish bash bosh
printf '%-20s#\n' "$@"

Com /bin/sh , modificando $@ no local:

set -- bish bash bosh
i=0
while [ "$i" -lt "$#" ]; do
    set -- "$@" "$( printf '%-20s#' "$1" )"
    shift
    i=$(( i + 1 ))
done

printf '%s\n' "$@"

A string de formatação printf %-20s reserva 20 caracteres para uma string justificada à esquerda.

Como uma função bash (4.3+):

pad_array () {
    local padlen=$1
    local -n localarray=$2

    local -a tmp
    local elem

    for elem in "${localarray[@]}"; do
        tmp+=( "$( printf '%-*s#' "$padlen" "$elem" )" )
    done

    localarray=( "${tmp[@]}" )
}

myarray=( bish bash bosh )
pad_array 20 myarray

printf '%s\n' "${myarray[@]}"

A função pad_array aqui também permite que você escolha a quantidade de preenchimento.

A matriz é passada por seu nome e é recebida pela função em uma variável de referência de nome. Isso significa que sempre que a referência de nome é acessada na função, a variável denominada é realmente usada.

    
por 13.06.2018 / 14:41
4

Se a mudança para zsh for uma opção:

$ array=(foo bar bàz '')
$ padded_array=(${(r:20:)array})
$ printf '<%s>\n' $padded_array
<foo                 >
<bar                 >
<bàz                 >
<                    >

Observe que o sinalizador de expansão do parâmetro (r:20:) right-padding é preenchido, mas também trunca 20 caracteres para strings mais longas.

Outra opção com zsh é fazer:

padded_array=()
(($#array)) && printf -v padded_array '%-20s' "$array[@]"

Ao contrário de bash , o preenchimento é feito em caracteres em oposição a byte e zsh suporta argumentos de array para a opção -v .

Observe que, se $array contiver apenas um elemento, $padded_array será convertido em escalar.

Para preenchimento + truncamento, substitua %-20s por %-20.20s .

    
por 13.06.2018 / 15:12
1

Você está cometendo dois erros.

  1. Você está passando testArray para a função, depois cria a nova matriz na função, modifica-a, mas finalmente imprime a primeira matriz. Você quer imprimir os resultados modificados, certo?

  2. Seu script não faz o que você espera, porque você usa essa construção:

     array[$counter]=${array[$counter]/%/$l16}
    

    Esta é a substituição do padrão da forma geral ${parameter/pattern/string} . Você não precisa substituir nada. Você tem o sufixo pronto neste momento, só precisa anexá-lo como:

    array[$counter]="${array[$counter]}${l16}"
    

Você pode acessar a matriz array fora da função, pois a matriz não é declarada como local. Então você pode simplesmente usar:

echo -e "${array[0]}"

etc.

    
por 13.06.2018 / 15:08
1

Para passar variáveis por referência em bash 4.3+, você pode usar typeset -n :

pad_array() {
  typeset -n _array="$1"
  typeset _n="$2" _pad _i
  printf -v _pad '%*s' "$_n"

  for _i in "${!_array[@]}"; do
    ((${#_array[_i]} < _n)) || continue # remove this line to also truncate
    _array[_i]+=$_pad
    _array[_i]=${_array[_i]:0:_n}
  done
}

array=(foo b bàz '')
pad_array array 20

O recurso typeset -n nameref vem de ksh93 no entanto, note que ao contrário de ksh93 , o acima não funcionará para preencher variáveis de array cujo nome é usado na função (portanto o prefixo de sublinhado para limitar o risco de conflito).

    
por 13.06.2018 / 15:33
0

Se você estiver disposto a colocar um limite superior arbitrário sobre o número de espaços para adicionar (e você obviamente é, como sua instrução case não pode manipular um tamanho maior que 20), e você está falando apenas de bytes (e não caracteres , que podem ser multi-byte), Uma maneira simples de preencher uma variável com um determinado tamanho é:

# 75 spaces
spaces="                                                                           "
    ︙
length=20
    ︙
newValue=$(expr substr "$value$spaces" 1 "$length")
    
por 13.06.2018 / 23:59
0

ideia principal

for elem in "${localarrayname[@]}"; do
    localtoarray+=(   "$(printf '<%-*.*s>\n' "$p" "$p" "$elem")"   )
done

var simples

Usar o printf para adicionar uma contagem variável de espaços é muito simples (o asterisco é a contagem):

$ printf '<%-*s>'   8   hello    ; echo
<hello   >

Poderia até mesmo ser usado para estender ou cortar uma string para uma contagem:

$ printf '<%-*.*s>'   8 8   "TestingHello"    ; echo
<TestingH>

Sequência à direita

Usando a substituição de variáveis, podemos adicionar uma string fixa a uma variável:

$ var=hello
$ printf '<%s>\n' "${var/%/"xxx"}"
<helloxxx>

Isso pode ser expandido para uma matriz de valores:

$ var=( hello world test )
$ printf '<%s>\n' "${var[@]/%/"xxx"}"
<helloxxx>
<worldxxx>
<testxxx>

Matriz de valores.

No entanto, para adicionar uma contagem variável de espaços a um array, precisamos usar um loop.
Podemos usar uma função para passar a lista de valores (somente impressão):

padarray(){ p=$1; shift
            for elem; do
                printf '<%-*.*s>\n' "$p" "$p" "$elem"
            done
          }

array=( foo qux quuux alongerstring )
padarray 15 "${array[@]}"

Imprime:

$ ./script
<foo            >
<qux            >
<quuux          >
<alongerstringto>

Matriz indireta.

Para passar os argumentos para uma nova matriz usando os nomes das matrizes (indiretos):

padarray(){ local p=$1;
            local -n localarrayname=$2 localtoarray=$3
            localtoarray=()
            for elem in "${localarrayname[@]}"; do
                localtoarray+=(   "$(printf '<%-*.*s>\n' "$p" "$p" "$elem")"   )
            done
          }

array=( foo qux quuux alongerstringtotest )
padarray 12 array paddedarray
printf '%s\n' "${paddedarray[@]}"

Que na execução imprime um resultado semelhante:

$ ./script
<foo         >
<qux         >
<quuux       >
<alongerstrin>

Melhor contagem de pads.

Adicionando um número de contagem de caracteres de preenchimento mais robusto ( $p ):

p=${1//[!0-9]}                    # Select only numeric digits.
(( 0<=p && p<=999 )) && return 2  # Return on error. Count out of range.

Final

Terminamos com este script:

#!/bin/bash

padarray(){ local p=${1//[!0-9]}                       # Select only numeric digits.
            p=${p:-20}                                 # Make the default count = 20.
            local -n localarrayname=$2 localtoarray=$3 # Define indirect (local) array names.
            localtoarray=()                            # Clean the resulting array.
            (( 0<=p && p<=999 )) || return 2           # Return on error. Count out of range.
            for elem in "${localarrayname[@]}"; do
                localtoarray+=(   "$(printf '<%-*.*s>\n' "$p" "$p" "$elem")"   )
            done
          }


array=( bash bish bosh "" foo qux quuux "A Longer String To Test" )
padarray 20 array paddedarray
printf '%s\n' "${paddedarray[@]}"
    
por 14.06.2018 / 09:59