Newsgroup: it.comp.os.linux.sys
Titolo: bash completion & shell scripting

Sto facendo esperimenti con il bash completion (non so proprio cosa
fare dite voi vero?)
Funziona, ma ho un problema:
        if [ $(echo ${COMP_WORDS[@]} | grep -c '^myset interface
ethernet') == 1 ]; then
                local param=$(cat /proc/net/dev | grep -v '|' | cut -
f1 -d':' | tr '\n' ' ' | tr -s ' ')
                COMPREPLY=( $(compgen -W "${param}" -- ${cur}) )
                return 0
        fi
        if [ $(echo ${COMP_WORDS[@]} | grep -c '^myset interface
ethernet [a-zA-Z0-9]\{1,\}$') == 1 ]; then
                local param="address speed duplex"
                COMPREPLY=( $(compgen -W "${param}" -- ${cur}) )
                return 0
        fi
quando, ad esempio, il comando viene completato in
# myset interface ethernet
e spingo TAB, giustamente mi viene eseguito il comando presente in
"param" del primo blocco IF.
Così facendo, però, il secondo blocco if non viene mai eseguito, in
quanto la stringa
"myset interface ethernet" sarà sempre presente, quindi la prima
condizione sarà sempre vera.
Come diavolo risolvo questa cosa?
Devo fare in modo che
# myset interface ethernet e[TAB]
mi venga completato con il primo blocco if (quindi con eth0, eth1 e
così via presi da /proc/dev/interfaces)
e facendo
# myset interface ethernet eth0 [TAB]  (quindi avendo completato il
primo blocco if)
mi venga completato con il secondo blocco if (quindi con address,
speed, duplex)



metti prima il secondo blocco if.

 metti prima il secondo blocco if.
eheheh
Questa non è proprio una soluzione.
Comunque sia, già fatto, non funge ugualmente.



A me funziona:
$ complete -F compfun myset
$ myset interface ethernet <tab><tab>
dummy0   eth0     lo       sit0     tap1000  tunl0
$ myset interface ethernet eth0 <tab><tab>
address  duplex   speed

 address  duplex   speed
Allora ho qualche altro problema, mi mandi il codice che hai usato te?

 address  duplex   speed
Funziona in parte.
Devi scrivere manualmente il nome dell'interfaccia, quello non riesci
a completarlo. Ed è il mio problema.
Prova con
# myset interface ethernet et<tab><tab> e vedrai che non fa niente.

 # myset interface ethernet et<tab><tab> e vedrai che non fa niente.
Ah certo, se usi -F quello sei tu che devi gestirlo nella funzione, in base
al contenuto gia' esistente della linea...

 al contenuto gia' esistente della linea...
Perchè, vedi alternative all'uso di -F ?
Se inverto le righe di cui sopra (quindi le metto come nel mio esempio
iniziale) mi funziona
il completamento delle interfacce (myset interface ethernet
et<tab><tab>) ma poi non
funziona il completamento di 'address, duplex, e così via'.
Se invece inverto i due if, funziona address, duplex, etc, ma non
funziona il completamento delle interfacce.



Ok, adesso ho visto meglio. Avevi un problema di quoting. Prova cosi'
(sorvolo sulle questioni stilistiche, e ce ne sarebbe un bel po' da dire):
compfun() {
  if [ $(echo "${COMP_WORDS[@]}" | \
    grep -c '^myset interface ethernet [[:alnum:]]\{1,\} ') -eq 1 ]; then
    local param="address speed duplex"
    COMPREPLY=( $(compgen -W "${param}" -- ${COMP_WORDS[4]}) )
    return 0
  fi
  if [ $(echo "${COMP_WORDS[@]}" | \
    grep -c '^myset interface ethernet') -eq 1 ]; then
    local param=$(cat /proc/net/dev | grep -v '|' | \
                  cut -f1 -d':' | tr '\n' ' ' | tr -s ' ')
    COMPREPLY=( $(compgen -W "${param}" -- ${COMP_WORDS[3]}) )
    return 0
  fi
}
Ovviamente devi aggiustare l'espressione regolare del primo blocco if per
controllare che ci siano esattamente 5 parole, altrimenti completa
all'infinito con "address speed duplex", ma dovrebbe comunque essere un
punto di partenza.

 punto di partenza.
Ora provo.
Per le questioni stilistiche non sorvolare, dimmi pure.
Altrimenti qui non si impara mai e dato che sto facendo semplici
esperimenti, tanto
vale imparare qualcosa....

 punto di partenza.
Funziona, ma sinceramente non vedo modifiche nella regexp rispetto
alla mia....
L'unica differenza che noto è che tu hai usato [[:alnum:]]\{1,\}
anzichè il mio
elenco di caratteri...

 alla mia....
Le modifiche sono nella regex e nel quoting. Quando bash invoca la funzione,
l'array COMP_WORDS contiene anche l'elemento che si sta tentando di
completare, eventualmente vuoto.
Esempio: voglio completare il secondo argomento per la riga di comando 
comando arg1
Voglio che il completamento automatico funzioni se ho dato ALMENO uno spazio
dopo "arg1" (ovviamente).
Se premo tab quando il cursore e' immediatamente dopo l'1 di "arg1",
COMP_WORDS avra' due elementi: "comando" e "arg1". Non voglio completare in
questo caso.
Se premo tab quando ho dato almeno uno spazio dopo arg1, COMP_WORDS avra'
TRE elementi: "comando","arg1", e un elemento vuoto (""). In questo caso,
voglio completare, mostrando tutti i valori possibili per il secondo
argomento. 
Se, dopo "arg1" e gli spazi, scrivo qualcosa, COMP_WORDS avra' ancora tre
elementi, i primi due come prima, e il terzo con la parte che ho scritto
come secondo argomento. Anche in questo caso, voglio completare in base a
quel poco che ho gia' scritto nel secondo argomento.
Quindi bisogna fare due controlli per fare scattare compgen: primo, che
COMP_WORDS abbia TRE elementi, secondo, che la parte di riga scritta fino a
quel momento faccia match con l'espressione regolare (estesa) 
^comando +arg1 +[[:alnum:]]*$'
Se si usa il comando echo con un array, bash produce una stringa con gli
elementi dell'array separati da uno spazio. Siccome ogni token della riga
diventa un elemento dell'array, l'espressione regolare di cui sopra si puo'
semplificare in
^comando arg1 [[:alnum:]]*$'
Qui si vede dove e' importante il problema di quoting. Se faccio
echo ${COMP_WORDS[@]}
senza quotare, bash esegue word splitting e rimuove gli spazi alla fine
della stringa, quindi se ho solo dato uno spazio dopo arg1 ma non ho
scritto niente del secondo argomento, quello che grep vede e' 
^comando arg1$
che ovviamente non fa match con la regex di cui sopra. Se invece quotiamo
echo "${COMP_WORDS[@]}"
bash non fa word splitting e, siccome COMP_WORDS contiene TRE elementi (il
terzo vuoto), quello che grep vede e' questa stringa:
^comando arg1 $
che fa match con l'espressione, e si puo' quindi usare compgen.
Nota che al posto di usare echo "${COMP_WORDS[@]}" si potrebbe fare un test
diretto sul contenuto di COMP_LINE, o usare COMP_CWORD, etc. (man bash).
Parlando di compgen, bisogna chiamarlo passandogli come argomento la parola
da completare (${COMP_WORDS[2]} in questo esempio). Tu lo chiamavi con
${cur}, che non e' definito da nessuna parte e quindi era sempre vuoto.
Riepilogando, nel tuo caso si potrebbe quindi fare una cosa del genere (che
e' comunque ancora migliorabile):
compfun() {
  case ${#COMP_WORDS[@]} in
  5) if ( echo "${COMP_WORDS[@]}" | \
 egrep -q '^myset interface ethernet [[:alnum:]]+ [[:alnum:]]*$' ); then
       param="address speed duplex"
       COMPREPLY=( $(compgen -W "${param}" -- ${COMP_WORDS[4]}) )
     fi
     ;;
  4) if ( echo "${COMP_WORDS[@]}" | \
 egrep -q '^myset interface ethernet [[:alnum:]]*$' ); then
       param=$(awk -F: 'NR>2{print $1}' /proc/net/dev)
       COMPREPLY=( $(compgen -W "${param}" -- ${COMP_WORDS[3]}) )
     fi
     ;;
# 3)  altre operazioni per completare
#     altre parole nella riga di comando
# 2) etc.
  *)         # non faccio nulla negli altri casi
    ;;
  esac
}


Innanzitutto grazie.
Dunque, teoricamente usando un case sul numero di argomenti come hai
fatto te dovrei semplificarmi la vita: se gli argomenti sono 4, una
eventuale regexp troppo permissiva (ad esempio .*) indicata nel case
da 3 elementi sarebbe ignorata.
Uhm, potrebbe funzionare....