2009-08-25 4 views
3

Je préfère programmer mes scripts bash pour qu'ils soient aussi procéduraux que possible. Une difficulté que j'ai rencontrée en essayant de le faire se produit lors du passage des données de tableau entre les fonctions, une tâche qui n'est pas bien prise en charge dans bash.Quel est le moyen le plus sûr d'initialiser des tableaux bash avec des valeurs entre guillemets à partir de la sortie de la fonction?

A titre d'exemple, il est trivial initiale d'un tableau en bash avec de multiples codées en dur, les valeurs citées, dont chacun peut contenir plusieurs mots:

declare -a LINES=("Hello there" "loyal user") 
echo "Line 0: '${LINES[0]}'" 
echo "Line 1: '${LINES[1]}'" 
# Line 0: 'Hello there' 
# Line 1: 'Loyal user' 

Toutefois, le remplacement des valeurs codées en dur avec le sortie d'une fonction semble ne pas fonctionner si bien:

getLines() { 
    echo "\"Hello there\" \"loyal user\"" 
} 

local LINE_STR=$(getLines) 
declare -a LINES=(${LINE_STR}) 
echo "Line 0: '${LINES[0]}'" 
echo "Line 1: '${LINES[1]}'" 
# Line 0: '"Hello' 
# Line 1: 'there"' 

J'ai essayé presque toutes les permutations des états bash a permis de surmonter ce problème. Une approche qui semble bien fonctionner est 'eval':

local LINE_STR=$(getLines) 
eval declare -a LINES=(${LINE_STR}) 
echo "Line 0: '${LINES[0]}'" 
echo "Line 1: '${LINES[1]}'" 
# Line 0: 'Hello there' 
# Line 1: 'loyal user' 

Cependant, cette approche est travaillée avec des problèmes de sécurité, comme l'a montré ici:

emulateUnsafeInput() { 
    echo "\"\`whoami\` just got haxxored\" \"Hahaha!\"" 
} 

local LINE_STR=$(emulateUnsafeInput) 
eval declare -a LINES=("${LINE_STR}") 
echo "Line 0: '${LINES[0]}'" 
echo "Line 1: '${LINES[1]}'" 
# Line 0: 'root just got haxxored' 
# Line 1: 'Hahaha!' 

'read -a' apparaît comme un possible solution, bien que problématique parce que 'read' fonctionnera dans une sous-couche lorsque des données y sont acheminées, séparant efficacement sa pile variable de celle du script appelant.

Quelles solutions devrais-je envisager pour atténuer les problèmes de sécurité de l'approche 'eval'? J'ai inclus le script suivant qui démontre la myriade d'approches que j'ai essayé:

#!/bin/bash 

getLines() { 
    echo "\"Hello there\" \"loyal user\"" 
} 

emulateUnsafeInput() { 
    echo "\"\`whoami\` just got haxxored\" \"Hahaha!\"" 
} 

execute() { 
(
    echo Test 01 
    declare -a LINES=("Hello there" "loyal user") 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'Hello there' 
    # Line 1: 'loyal user' 
);(
    echo Test 02 
    local LINE_STR=$(getLines) 
    declare -a LINES=(${LINE_STR}) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: '"Hello' 
    # Line 1: 'there"' 
);(
    echo Test 03 
    local LINE_STR=$(getLines) 
    declare -a LINES=("${LINE_STR}") 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: '"Hello there" "loyal user"' 
    # Line 1: '' 
);(
    echo Test 04 
    local LINE_STR=$(getLines) 
    eval declare -a LINES=(${LINE_STR}) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'Hello there' 
    # Line 1: 'loyal user' 
);(
    echo Test 05 
    local LINE_STR=$(getLines) 
    eval declare -a LINES=("${LINE_STR}") 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'Hello there' 
    # Line 1: 'loyal user' 
);(
    echo Test 06 
    local LINE_STR=$(getLines) 
    declare -a LINES=($(echo ${LINE_STR})) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: '"Hello' 
    # Line 1: 'there"' 
);(
    echo Test 07 
    local LINE_STR=$(getLines) 
    declare -a LINES=($(echo "${LINE_STR}")) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: '"Hello' 
    # Line 1: 'there"' 
);(
    echo Test 08 
    local LINE_STR=$(getLines) 
    declare -a LINES=($(eval echo ${LINE_STR})) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'Hello' 
    # Line 1: 'there' 
);(
    echo Test 09 
    local LINE_STR=$(getLines) 
    declare -a LINES=($(eval echo "${LINE_STR}")) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'Hello' 
    # Line 1: 'there' 
);(
    echo Test 10 
    local LINE_STR=$(emulateUnsafeInput) 
    eval declare -a LINES=(${LINE_STR}) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'root just got haxxored' 
    # Line 1: 'Hahaha!' 
);(
    echo Test 11 
    local LINE_STR=$(emulateUnsafeInput) 
    eval declare -a LINES=("${LINE_STR}") 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'root just got haxxored' 
    # Line 1: 'Hahaha!' 
);(
    echo Test 12 
    local LINE_STR=$(emulateUnsafeInput) 
    declare -a LINES=($(eval echo ${LINE_STR})) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'root' 
    # Line 1: 'just' 
);(
    echo Test 13 
    local LINE_STR=$(emulateUnsafeInput) 
    declare -a LINES=($(eval echo "${LINE_STR}")) 
    echo "Line 0: '${LINES[0]}'" 
    echo "Line 1: '${LINES[1]}'" 
    # Line 0: 'root' 
    # Line 1: 'just' 
) 
} 

execute 

Répondre

1

Pour la fonction de données utilisation echo -e et la séparation des données avec les nouvelles lignes:

getLines() { echo -e "\"Hello there\"\n\"loyal user\""; } 

Pour lire les données, l'utilisation processus de substitution et de redirection:

i=0 
while read -r 
do 
    arr[i++]=$REPLY 
done < <(getLines) 
# Line 0: '"Hello there"' 
# Line 1: '"loyal user"' 

Cela laisse les guillemets autour des chaînes, cependant.

Basé sur les techniques de here.

2

Voici handles spaces in array elements correctly:

#! /bin/bash 

# $ ./return_an_array.sh 
# ./return_an_array.sh: line 9: declare: returned_array: not found 
# declare -a returned_array='([0]="one" [1]="two three")' 

return_an_array() 
{ 
    local -a an_array=('one' 'two three') 
    declare -p an_array 
} 

declare -p returned_array 
eval $(return_an_array | sed -e 's/^\(declare -a \)[^=]*=/\1 returned_array=/') 
declare -p returned_array 
1

Si vous êtes inquiet au sujet des données malveillantes arbitraires, de fonctions, vous devez vous attendre simplement et analyser des chaînes avec le séparateur \0 (alias NUL.). C'est le seul caractère qui ne peut faire partie d'aucune variable, donc il n'y a aucun moyen qu'il y aura des collisions avec des données réelles.

haxxorz() { 
    printf '%s\0' whoami 
    printf '%s\0' '\`whoami\`' 
    printf '%s\0' "\`whoami\`" 
    printf '%s\0' "\"\`whoami\` is haxxor-proof"'!'"\"" 
    printf '%s' 'Trying to poison the $REPLY variable with a missing separator' 
} 

index=0 
while IFS= read -r -d '' || [ -n "$REPLY" ] 
do 
    array[index++]="$REPLY" 
done < <(haxxorz) 

for element in "${array[@]}" 
do 
    echo "$element" 
done 

echo "${REPLY:-REPLY is empty}" 

Résultat:

whoami 
\`whoami\` 
`whoami` 
"`whoami` is haxxor-proof!" 
Trying to poison the $REPLY variable with a missing separator 
REPLY is empty 

Je l'aime quand un plan se rassemble!

Questions connexes