tests-always-included / wick

Bash-only IT automation, machine provisioning
Other
69 stars 12 forks source link

IFS= appears to merge all elements #113

Closed baelish closed 7 years ago

baelish commented 7 years ago
>cat testServiceIssue 
#!/usr/bin/env bash

. /usr/local/lib/wick-infect
wickStrictMode

testFunction() {
    wickGetOptions options "$@" || return $?
    wickGetArgument action 0 "$@" || return $?
    wickGetArguments unparsed "$@" || return $?
    if [[ ${#unparsed[@]} -lt 1 ]]; then
        wickError "Specify action to take and the service name to act upon." || return $?

        return 1
    fi

    if [[ ${#unparsed[@]} -lt 2 ]]; then
        wickError "Must specify service name to act upon" || return $?

        return 1
    fi

        unparsedClone=("${unparsed[@]}")
        unparsedClone2=("${unparsed[@]}")
        unparsed=("${unparsed[@]:1}")
        echo "unparsed:" "${unparsed[*]}"
        IFS= unparsedClone=("${unparsedClone[@]:1}")
        echo "unparsedClone:" "${unparsedClone[*]}"
        IFS=" " unparsedClone2=("${unparsedClone2[@]:1}")
        echo "unparsedClone2:" "${unparsedClone2[*]}"

}

testFunction machineName action service
>bash testServiceIssue 
unparsed: action
service
unparsedClone: actionservice
unparsedClone2: action service
>
baelish commented 7 years ago

Thanks @quantumew

fidian commented 7 years ago

This isn't the real problem.

#!/usr/bin/env bash
set -eu
set -E &> /dev/null
set -o pipefail &> /dev/null

array=(one two three)

testNoIfs=("${array[@]:1}")
set | grep ^testNoIfs=
echo "testNoIfs[@]:" "${testNoIfs[@]}"
echo "testNoIfs[*]:" "${testNoIfs[*]}"
set | grep ^IFS=

IFS= testIfsEmpty=("${array[@]:1}")
set | grep ^testIfsEmpty=
echo "testIfsEmpty[@]:" "${testIfsEmpty[@]}"
echo "testIfsEmpty[*]:" "${testIfsEmpty[*]}"
set | grep ^IFS=

IFS=" " testIfsSpace=("${array[@]:1}")
set | grep ^testIfsSpace=
echo "testIfsSpace[@]:" "${testIfsSpace[@]}"
echo "testIfsSpace[*]:" "${testIfsSpace[*]}"
set | grep ^IFS=

Results:

testNoIfs=([0]="two" [1]="three")
testNoIfs[@]: two three
testNoIfs[*]: two three
IFS=$' \t\n'
testIfsEmpty=([0]="two" [1]="three")
testIfsEmpty[@]: two three
testIfsEmpty[*]: twothree
IFS=
testIfsSpace=([0]="two" [1]="three")
testIfsSpace[@]: two three
testIfsSpace[*]: two three
IFS=' '

You can see that the IFS is being set. Because the line doesn't contain a command to run, the IFS setting is committed to the environment instead of being reverted. In addition to this bug being not fixed by the proposed solution you provided, it is also not fixed by the solution I provided earlier.

Right fix:

oldIfs=$IFS
IFS=
array=("${array[@]:1}")
IFS=$oldIfs

Disgusting, but necessary. I'll start work on the fixes.

baelish commented 7 years ago

Just had a thought, would it be less disgusting to do: IFS= ARGS=("${ARGS[@]:1}") : Just tested it and it seems to work just fine too:

>cat /tmp/test.sh                                                                                                                                                                      
#!/usr/bin/env bash

ARGS=("$@")
IFS=$' .\t,\n'

echo ":$IFS:"

IFS= ARGS=("${ARGS[@]:1}") :

echo "Args: ${ARGS[@]}"

echo ":$IFS:"

~
Tyrion$>bash /tmp/test.sh blah blah blah
: .     ,
:
Args: blah blah blah
: .     ,
:

>
fidian commented 7 years ago

The array itself (ARGS) was not changed. When you use ("${ARGS[@]:1}") the first "blah" should have been removed.

baelish commented 7 years ago

Bother....