2011-05-06 12 views
16

Un esempio inventato ... datoBash male sostituzione con subshell e substring

FOO="/foo/bar/baz" 

questo funziona (in bash)

BAR=$(basename $FOO) # result is BAR="baz" 
BAZ=${BAR:0:1}  # result is BAZ="b" 

questo non lo fa

BAZ=${$(basename $FOO):0:1} # result is bad substitution 

La mia domanda qual è la regola che questa [sostituzione subshell] deve essere valutata in modo errato? E qual è il modo corretto, se esiste, per farlo in 1 hop?

risposta

10

Prima di tutto, si noti che quando si dice questo:

BAR=$(basename $FOO) # result is BAR="baz" 
BAZ=${BAR:0:1}  # result is BAZ="b" 

il primo bit nel costrutto per BAZ è BAR e non il valore di che si vuole prendere il primo carattere di. Quindi, anche se bash permettesse che i nomi delle variabili contengano caratteri arbitrari, il risultato nella seconda espressione non sarebbe quello che vuoi.

Tuttavia, per quanto riguarda la regola che impedisce questo, mi permetta di citare dalla pagina man di bash:

DEFINITIONS 
     The following definitions are used throughout the rest of this docu‐ 
     ment. 
     blank A space or tab. 
     word A sequence of characters considered as a single unit by the 
       shell. Also known as a token. 
     name A word consisting only of alphanumeric characters and under‐ 
       scores, and beginning with an alphabetic character or an under‐ 
       score. Also referred to as an identifier. 

Poi un po 'più tardi:

PARAMETERS 
     A parameter is an entity that stores values. It can be a name, a num‐ 
     ber, or one of the special characters listed below under Special Param‐ 
     eters. A variable is a parameter denoted by a name. A variable has a 
     value and zero or more attributes. Attributes are assigned using the 
     declare builtin command (see declare below in SHELL BUILTIN COMMANDS). 

E più tardi, quando si definisce la sintassi stai chiedendo informazioni su:

${parameter:offset:length} 
      Substring Expansion. Expands to up to length characters of 
      parameter starting at the character specified by offset. 

Quindi le regole come articolate nella manpage dicono che loIl costruttodeve avere un parametro come prima parte e che un parametro può essere solo un nome, un numero o uno dei pochi caratteri di parametro speciali. $(basename $FOO) non è una delle possibilità consentite per un parametro.

Per quanto riguarda un modo per eseguire questa operazione in un compito, utilizzare una pipe per altri comandi come indicato in altre risposte.

6

Le forme modificate di sostituzione parametri come ${parameter#word} possono modificare solo un parametro, non una parola arbitraria.

In questo caso, si potrebbe inviare l'output di basename a un comando dd, come

BAR=$(basename -- "$FOO" | dd bs=1 count=1 2>/dev/null) 

(Se si desidera un conteggio più elevato, aumentare count e non bs, altrimenti si rischia di ottenere un minor numero di byte di richiesta .)

Nel caso generale, non c'è modo di fare cose come questa in un compito.

6

Non riesce perché ${BAR:0:1} è un'espansione variabile. Bash si aspetta di vedere un nome di variabile dopo ${, non un valore.

Non sono a conoscenza di un modo per farlo in una singola espressione.

0

$ {stringa: 0: 1}, stringa deve essere un nome di variabile

ad esempio:

FOO = "/ foo/bar/baz"

baz = "foo"

BAZ = eval echo '${'"$(basename $FOO)"':0:1}'

echo $ BAZ

il risultato che s 'f'

1

Come altri hanno già detto, il primo parametro di $ {} deve essere un nome di variabile. Ma puoi usare un'altra subshell per approssimare quello che stai cercando di fare.

Invece di:

BAZ=${$(basename $FOO):0:1} # result is bad substitution 

Usa:

BAZ=$(_TMP=$(basename $FOO);${_TMP:0:1}) # this works 
0

Una soluzione artificiosa per il vostro esempio inventato:

BAZ=$(expr $(basename $FOO) : '\(.\)') 

come in

$ FOO=/abc/def/ghi/jkl 
$ BAZ=$(expr $(basename $FOO) : '\(.\)') 
$ echo $BAZ 
j 
Problemi correlati