[TxMt] Mercurial bundle

Allan Odgaard throw-away-1 at macromates.com
Thu May 4 04:41:31 UTC 2006


On 4/5/2006, at 0:16, Fred B. wrote:

>> However, there are some problems with this line. First,  
>> $TM_SELECTED_FILES should be quoted. Second, the eval causes the  
>> line to be re-interpreted after initial variable expansion, this  
>> is why you get an error without the single quotes.
> I didn't quote  $TM_SELECTED_FILES because it was not quoted in the  
> svn bundle and I didn't have any problem even with paths with space  
> in them. It works when quoted too, so I'll quote it anyway.

Here single spaces in file names would not be a problem. But had  
there been two consecutive spaces, or the use of tabs or newlines,  
then it would have failed.

The reason why a variable needs to be quoted is, that the shell will  
“split it” into multiple arguments, based on spaces, newlines, and  
tabs. So for example:

    # $'…' is an escape-code supporting string
    f=$' abc \t def '

    tst () {
       # iterate over arguments
       for arg in "$@"; do echo "» $arg"; done
    }

Now if we do:

    tst $f

We get:

    » abc
    » def

So tst receives two arguments. Had we instead done:

    tst "$f"

We get:

    »  abc 	 def

That is, now it gets only one argument, since bash will not split $f  
after expansion.

The eval prefix makes bash re-evaluate the line, after it has done  
the initial expansion of variables, so if we do:

    eval tst "$f"

We again get:

    » abc
    » def

So we are back to getting the variable split.

The value of TM_SELECTED_FILES is something like: 'file 1' 'file 2'  
'file 3'.

This means, that even though bash does split the variable into space- 
separated arguments, the apostrophes remain, and eval will see these.  
For example:

    f="'file 1' 'file 2' 'file 3'"
    eval tst $f

This gives:

    » file 1
    » file 2
    » file 3

I.e. it works, but then try:

    f="'file  1' 'file   2' 'file    3'"
    eval tst $f

And again, we get:

    » file 1
    » file 2
    » file 3

Which is wrong. This is because after initial expansion, the line has  
become:

    eval tst 'file 1' 'file 2' 'file 3'

And that is now being evaluated. Had we quoted $f, it would have  
correctly become:

    eval tst 'file  1' 'file   2' 'file    3'

Don’t know if that made it any clearer. I should add that bash splits  
the input based on the characters found in the IFS variable (input  
field separator). So it is possible to change this to have it split  
input differently. For example, let’s say we have this function  
“generating” arguments:

    args () {
       echo argument one
       echo argument two
       echo argument three
    }

If we do:

    tst $(args)

We get 6 arguments printed, since bash will split on both the spaces  
and the newlines in the result from args. Here quoting won’t help, as  
that will turn all the output into one argument, i.e.:

    tst "$(args)"

Gives us:

    » argument one
    argument two
    argument three

What we can however do is, change IFS to have bash only split on  
newlines, and lose the quotes:

    IFS=$'\n'
    tst $(args)

And we get:

    » argument one
    » argument two
    » argument three

> None of the solutions work.
> The first needs or has an unneeded single quote [...]

That’s what I was referring to with bash being broken. It has a  
problem with quotes/escapes in variable substitutions. The example I  
gave was for zsh.

> [...] The second doesn't escape the path, as if there wasn't the  
> dummy variable.

Sorry, it should have been:

    dummy='"$HG_STYLE"'

> But, I just found that $HG_STYLE was 'too' escaped in the other  
> line (without eval), maybe that was the problem.

Not in the snippet you sent to this list.




More information about the textmate mailing list