Completion called within command substitution

2009-09-25 Thread Freddy Vulto
Configuration Information [Automatically generated, do not change]:
Machine: i486
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='i486'
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='i486-pc-linux-gnu'
-DCONF_VENDOR='pc' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash'
-DSHELL -DHAVE_CONFIG_H   -I.  -I../bash -I../bash/include
-I../bash/lib   -g -O2 -Wall
uname output: Linux myhost 2.6.26-2-686 #1 SMP Wed Aug 19 06:06:52 UTC
2009 i686 GNU/Linux
Machine Type: i486-pc-linux-gnu

Bash Version: 3.2
Patch Level: 39
Release Status: release

When completing command substitution:

$ a $(b c

the command completion for `a' is invoked, whereas I'd expect the
command completion for `b' to be invoked.
I would expect the same behaviour as with process substitution, which
invokes the completion for `b':

$ a <(b c

Steps to reproduce:

1.  Create functions `_a' and `_b' to complete `a' and `b':

_a() { echo a; }
_b() { echo b; }
complete -F _a a
complete -F _b b

2.  Complete `b' within command substition:

$ a $(b c  # This returns `a', where I'd expected `b'?

3.  Complete `b' within process substitution:

$ a <(b c  # This returns `b' all right.


Regards,

Freddy Vulto
http://fvue.nl




Re: Strange compgen behaviour

2009-09-25 Thread Bernd.Eggink

Chet Ramey schrieb:

Bernd Eggink wrote:

Chet Ramey schrieb:
Hm, compgen appears to behave strange if words contain whitespace. 

Well, it splits the argument to -W on $IFS as documented.  What other
strange behavior do you see? 

For example, this:

function _aha
{
local list="a b:c d:e f"
COMPREPLY=($(IFS=: compgen -W "$list"))
}

complete -F _aha aha

Typing aha  cycles through 6 items a, b, c, d, e, f, whereas I
would expect 3 items 'a b', 'c d', 'e f'. It looks like compgen splits
the argument to -W on $IFS _and_ whitespace. Or am I missing something?


I don't think you're considering the interaction between compgen,
command substitution, and compound array assignment.

I'll use the assignment to `list' you used above for the explanation.

compgen performs word splitting using $IFS and then outputs the results,
one word per line:

$ IFS=: compgen -W "$list"
a b
c d
e f

Command substitution takes that, chops off the last newline, and, since
it's unquoted, performs word splitting using $IFS on the result:

$ recho $(IFS=: compgen -W "$list")
argv[1] = 
argv[2] = 
argv[3] = 
argv[4] = 
argv[5] = 
argv[6] = 

If you want to preserve the embedded newlines, you have to quote the
expansion (but see below):

$ recho "$(IFS=: compgen -W "$list")"
argv[1] = 

When performing compound array assignments, though, the words between
the parens undergo all the shell word expansions, including word splitting:

$ COMPREPLY=( $(IFS=: compgen -W "$list") )
$ recho "${comprep...@]}"
argv[1] = 
argv[2] = 
argv[3] = 
argv[4] = 
argv[5] = 
argv[6] = 

So you can split on just the embedded newlines by setting $IFS:

$ oifs="$IFS"
$ IFS=$'\n' COMPREPLY=( $(IFS=: compgen -W "$list") )
$ IFS="$oifs"
$ recho "${comprep...@]}"
argv[1] = 
argv[2] = 
argv[3] = 

Remember to save and restore $IFS, though, since the assignment to it
preceding the assignment to COMPREPLY will persist.


Chet,

thanks for the clarification. It's actually quite obvious and I should 
have seen it myself.


Regards,
Bernd




Re: Strange compgen behaviour

2009-09-25 Thread Mathias Dahl
> printf "%q\n" works fine here.

It does here too :), I misunderstood the syntax.

> Also, grep "${cur}" could probably be replaced by -path "*/${cur}*".

Indeed it can.

Smaller = better, thanks!

/Mathias


Re: Strange compgen behaviour

2009-09-25 Thread Chet Ramey
Bernd Eggink wrote:
> Chet Ramey schrieb:
>>> Hm, compgen appears to behave strange if words contain whitespace. 
>>
>> Well, it splits the argument to -W on $IFS as documented.  What other
>> strange behavior do you see? 
> 
> For example, this:
> 
> function _aha
> {
> local list="a b:c d:e f"
> COMPREPLY=($(IFS=: compgen -W "$list"))
> }
> 
> complete -F _aha aha
> 
> Typing aha  cycles through 6 items a, b, c, d, e, f, whereas I
> would expect 3 items 'a b', 'c d', 'e f'. It looks like compgen splits
> the argument to -W on $IFS _and_ whitespace. Or am I missing something?

I don't think you're considering the interaction between compgen,
command substitution, and compound array assignment.

I'll use the assignment to `list' you used above for the explanation.

compgen performs word splitting using $IFS and then outputs the results,
one word per line:

$ IFS=: compgen -W "$list"
a b
c d
e f

Command substitution takes that, chops off the last newline, and, since
it's unquoted, performs word splitting using $IFS on the result:

$ recho $(IFS=: compgen -W "$list")
argv[1] = 
argv[2] = 
argv[3] = 
argv[4] = 
argv[5] = 
argv[6] = 

If you want to preserve the embedded newlines, you have to quote the
expansion (but see below):

$ recho "$(IFS=: compgen -W "$list")"
argv[1] = 

When performing compound array assignments, though, the words between
the parens undergo all the shell word expansions, including word splitting:

$ COMPREPLY=( $(IFS=: compgen -W "$list") )
$ recho "${comprep...@]}"
argv[1] = 
argv[2] = 
argv[3] = 
argv[4] = 
argv[5] = 
argv[6] = 

So you can split on just the embedded newlines by setting $IFS:

$ oifs="$IFS"
$ IFS=$'\n' COMPREPLY=( $(IFS=: compgen -W "$list") )
$ IFS="$oifs"
$ recho "${comprep...@]}"
argv[1] = 
argv[2] = 
argv[3] = 

Remember to save and restore $IFS, though, since the assignment to it
preceding the assignment to COMPREPLY will persist.

Chet
-- 
``The lyf so short, the craft so long to lerne.'' - Chaucer
 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, ITS, CWRUc...@case.eduhttp://cnswww.cns.cwru.edu/~chet/




Re: Bug in array populating does not respect quotes

2009-09-25 Thread David Martin
Thank you for all and sorry for the noise, you were right.

David.

On Thu, Sep 24, 2009 at 6:38 PM, Chris F.A. Johnson
 wrote:
> On Thu, 24 Sep 2009, David Martin wrote:
>
>> Configuration Information [Automatically generated, do not change]:
>> Machine: x86_64
>> OS: linux-gnu
>> Compiler: gcc
>> Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64'
>> -DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-pc-linux-gnu'
>> -DCONF_VENDOR='pc' -DLOCALEDI$
>> uname output: Linux bristol 2.6.31 #10 SMP Thu Sep 10 17:59:29 CEST
>> 2009 x86_64 GNU/Linux
>> Machine Type: x86_64-pc-linux-gnu
>>
>> Bash Version: 4.0
>> Patch Level: 33 (debian bash-4.0-7)
>> Release Status: release
>>
>> Description:
>>         When populating an array from a string in a variable does not
>> handle quotes.
>>
>> Repeat-By:
>>
>> ~$ declare -a samplearray
>> ~$ samplearray=( x y 'z k')
>> ~$ echo ${samplearray[2]}
>> z k
>> ~$ samplestring="x y 'z k'"
>> ~$ samplearray=( $samplestring )
>
> eval "samplearray=( $samplestring )"
>
>> ~$ echo ${samplearray[2]}
>> 'z
>
> --
>   Chris F.A. Johnson, webmaster         
>   ===
>   Author:
>   Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)
>




Re: Strange compgen behaviour

2009-09-25 Thread Andreas Schwab
Mathias Dahl  writes:

> Got this to work:
>
>  files=$(find /home/mathias/Videos/movies/ -iname "*.avi" -type f -
> printf "%P\n" | grep "${cur}" | while read file; do
> printf %q "$file"
> echo
> done)
>
> With the %q option to printf it no longer accepts a \n so I needed the
> extra echo.

printf "%q\n" works fine here.

Also, grep "${cur}" could probably be replaced by -path "*/${cur}*".

Andreas.

-- 
Andreas Schwab, sch...@linux-m68k.org
GPG Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."




Re: Strange compgen behaviour

2009-09-25 Thread Mathias Dahl
> Interesting! Can I make it part of my pipe (in place of `sed') or need
> I change it to a for or while loop? This is what I have now:
>
> files=$(find /home/mathias/Videos/movies/ -iname "*.avi" -type f -
> printf "%P\n" | grep "${cur}" | sed "s/\\([][\\(\\) ,\']\\)/\\1/
> g")

Got this to work:

 files=$(find /home/mathias/Videos/movies/ -iname "*.avi" -type f -
printf "%P\n" | grep "${cur}" | while read file; do
printf %q "$file"
echo
done)

With the %q option to printf it no longer accepts a \n so I needed the
extra echo.

/Mathias


Re: Strange compgen behaviour

2009-09-25 Thread Mathias Dahl

> printf %q "$filename"
>
> will either insert backslashes in front of all the shell metacharacters,
> or $'...' quote the whole thing, or take some other action which renders
> a string "safe".  It's basically the opposite of eval.

Interesting! Can I make it part of my pipe (in place of `sed') or need
I change it to a for or while loop? This is what I have now:

files=$(find /home/mathias/Videos/movies/ -iname "*.avi" -type f -
printf "%P\n" | grep "${cur}" | sed "s/\\([][\\(\\) ,\']\\)/\\1/
g")

/Mathias


Re: Strange compgen behaviour

2009-09-25 Thread Mathias Dahl
> This has been an interesting topic!

I thought I should share the final version as well:

_mm() {
local cur files
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
files=$(find /home/mathias/Videos/movies/ -iname "*.avi" -type f -
printf "%P\n" | grep "${cur}" | sed "s/\\([][\\(\\) ,\']\\)/\\1/
g")
local IFS=$'\n'
COMPREPLY=(${files})
}

complete -F _mm mm

And the `mm' script needs to append the root folder, of course, like
so:

#!/bin/bash
mplayer "/home/mathias/Videos/movies/$1"

Works quite well. Maybe it will be useful to someone else too.

/Mathias


Re: Strange compgen behaviour

2009-09-25 Thread Greg Wooledge
On Thu, Sep 24, 2009 at 03:05:07PM -0700, Mathias Dahl wrote:
> I did not find any generic way to quote/escape file names so I
> hardcoded some chars I know exist in my file names and used sed:

printf %q "$filename"

will either insert backslashes in front of all the shell metacharacters,
or $'...' quote the whole thing, or take some other action which renders
a string "safe".  It's basically the opposite of eval.




Re: Strange compgen behaviour

2009-09-25 Thread Mathias Dahl
> Hm, I can't see any problem here. My version lets you pick any file in
> any subdir by simply typing the name (or part of it) without the
> directory part. After all, 'find -name' matches names, not paths (if you
> want to match full paths, use 'find -path'). I'd also rather use printf
> "%P\n" (capital P) instead of %p, the results look nicer (IMHO).

When I try with %P it works (better). However, when I do that and use
the -o filenames option I see only the filenames of the sub folders
and sometimes that name does not reflect what it is as well as the
folder does (if for example I have a folder called Cool Movie 2 and in
it a file called thefile.avi). So, it seems I did not really want what
I thought I wanted from the beginning :) The output I get now looks
like this:

...
filie_in_root_folder.avi
Some sub folder/file_in_sub_folder.avi
...

The only annoyance now is the hardcoded quoting (to handle spaces and
other "bad" chars) I do using sed, but I guess I have to live with it.

This has been an interesting topic!

/Mathias


Re: Real easy questions. Please answer

2009-09-25 Thread Chris F.A. Johnson
On Thu, 24 Sep 2009, eatsubway wrote:

> 
> sry i have a stupid question.
> 
> I have a variable and need to know how many items are in it.
> 
> for example:
> variable="abc xyz foo"
> what program can i call to print out 3
> 
> right now im doing this...
> 
> Counter()
> {
>   echo $#
> }
> Counter $IDs
> but that just seems stupid

   That's a perfectly good way of doing it, but you will want to turn
   off filename expansion if there's a chance that the value may
   contain wildcards:

set -f
Counter $IDs
set +f

-- 
   Chris F.A. Johnson, webmaster 
   ===
   Author:
   Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)




Re: Real easy questions. Please answer

2009-09-25 Thread Dave B
On Friday 25 September 2009 05:24:04 eatsubway wrote:
> sry i have a stupid question.
>
> I have a variable and need to know how many items are in it.
>
> for example:
> variable="abc xyz foo"
> what program can i call to print out 3
>
> right now im doing this...
>
> Counter()
> {
>   echo $#
> }
> Counter $IDs
> but that just seems stupid

Use an array, so you will even be able to store elements with spaces in them.

-- 
D.




Re: Strange compgen behaviour

2009-09-25 Thread Bernd Eggink

Mathias Dahl schrieb:

Hm, compgen appears to behave strange if words contain whitespace.
However, you don't need it, as you build the list yourself. Try this:

  _mm2() {
  local cur files
  cur=${COMP_WORDS[COMP_CWORD]}
  files=$(find /home/mathias/Videos/movies/ -iname "$cur*.avi" -type
f -printf "%P\n")
  local IFS=$'\n'
  COMPREPLY=($files)
  }


Ah, you're right of course, I can do the matching myself. I have yet
another version working now (had to change your latest suggestion and
use grep for matching because -name does not like full paths which
becomes the case here):

_mm() {
local cur files
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
files=$(find /home/mathias/Videos/movies/ -iname "*.avi" -type f -
printf "%p\n" | grep "${cur}")
local IFS=$'\n'
COMPREPLY=(${files})
}
complete -o filenames -F _mm mm

Now, this works almost. The remaining problem is that because `find'
finds file in subdirs (which I want, otherwise I could add the -
maxdepth option) as well, the `-o filenames' argument to `complete'
does not play well with it. I see the names of files in subdirs listed
when I type TAB (without path) but can never pick them without knowing
the name of the folder they are in.


Hm, I can't see any problem here. My version lets you pick any file in 
any subdir by simply typing the name (or part of it) without the 
directory part. After all, 'find -name' matches names, not paths (if you 
want to match full paths, use 'find -path'). I'd also rather use printf 
"%P\n" (capital P) instead of %p, the results look nicer (IMHO).


Regards,
Bernd

--
Bernd Eggink
http://sudrala.de