Quando posso utilizzare un IFS temporaneo per la divisione dei campi?


19

In bash, dì che hai var=a.b.c., quindi:

$ IFS=. printf "%s\n" $var
a.b.c

Tuttavia, tale utilizzo di IFSha effetto durante la creazione di un array:

$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c

Questo è molto comodo, certo, ma dove è documentato? Una rapida lettura delle sezioni su Array o Word Splitting nella documentazione di Bash non fornisce alcuna indicazione in entrambi i modi. Una ricerca per IFStramite della documentazione a pagina singola non fornisce alcun suggerimento su questo effetto sia.

Non sono sicuro di poter fare in modo affidabile:

IFS=x do something

E aspettiamo che IFSciò influisca sulla divisione dei campi.

Risposte:


28

L'idea di base è che i VAR=VALUE some-commandset VARper VALUEper l'esecuzione di some-commandquando some-commandè un comando esterno, e non ottiene più fantasia di quello. Se combini questa intuizione con una certa conoscenza di come funziona una shell, nella maggior parte dei casi dovresti trovare la risposta giusta. Il riferimento POSIX è "Comandi semplici" nel capitolo "Linguaggio di comando Shell" .

Se some-commandè un comando esterno , VAR=VALUE some-commandequivale a env VAR=VALUE some-command. VARviene esportato nell'ambiente di some-command, e il suo valore (o mancanza di valore) nella shell non cambia.

Se some-commandè una funzione , allora VAR=VALUE some-commandequivale a VAR=VALUE; some-command, ovvero l'assegnazione rimane attiva dopo che la funzione è tornata e la variabile non viene esportata nell'ambiente. La ragione di ciò ha a che fare con il design della shell Bourne (e successivamente con la compatibilità con le versioni precedenti): non aveva la possibilità di salvare e ripristinare valori variabili attorno all'esecuzione di una funzione. Non esportare la variabile ha senso poiché una funzione viene eseguita nella shell stessa. Tuttavia, ksh (inclusi ATT ksh93 e pdksh / mksh), bash e zsh implementano il comportamento più utile dove VARviene impostato solo durante l'esecuzione della funzione (viene anche esportato). In ksh , questo viene fatto se la funzione è definita con la sintassi kshfunction NAME …, non se è definito con la sintassi standard NAME (). In bash , questo viene fatto solo in modalità bash, non in modalità POSIX (se eseguito con POSIXLY_CORRECT=1). In zsh , questo viene fatto se l' posix_builtinsopzione non è impostata; questa opzione non è impostata per impostazione predefinita ma è attivata da emulate sho emulate ksh.

Se some-commandè incorporato, il comportamento dipende dal tipo di incorporato. I builtin speciali si comportano come funzioni. Gli incorporamenti speciali sono quelli che devono essere implementati all'interno della shell perché influenzano la shell di stato (ad es. Influenza il breakflusso di controllo, cdinfluenza la directory corrente, setinfluenza i parametri posizionali e le opzioni ...). Altri built-in sono integrati solo per prestazioni e praticità (principalmente - ad es. La funzione bash printf -vpuò essere implementata solo da un built-in) e si comportano come un comando esterno.

L'assegnazione avviene dopo l'espansione dell'alias, quindi se some-commandè un alias , espanderlo prima per trovare cosa succede.

Si noti che in tutti i casi, l'assegnazione viene eseguita dopo l'analisi della riga di comando, inclusa l'eventuale sostituzione di variabili sulla riga di comando stessa. Quindi var=a; var=b echo $varstampa a, perché $varviene valutato prima che l'incarico abbia luogo. E quindi IFS=. printf "%s\n" $varutilizza il vecchio IFSvalore per dividere $var.

Ho coperto tutti i tipi di comandi, ma c'è ancora un caso: quando non c'è nessun comando da eseguire , cioè se il comando è costituito solo da assegnazioni (e possibilmente reindirizzamenti). In tal caso, l'incarico rimane al suo posto . VAR=VALUE OTHERVAR=OTHERVALUEè equivalente a VAR=VALUE; OTHERVAR=OTHERVALUE. Quindi IFS=. arr=($var), IFSrimane impostato su .. Poiché è possibile utilizzare $IFSnell'assegnazione arrcon l'aspettativa che abbia già il suo nuovo valore, ha senso che il nuovo valore di IFSvenga utilizzato per l'espansione di $var.

In sintesi, è possibile utilizzare solo IFSper la suddivisione temporanea dei campi:

  • avviando una nuova shell o una subshell (ad es. third=$(IFS=.; set -f; set -- $var; echo "$3")è un modo complicato di fare third=${var#*.*.}tranne che si comportano diversamente quando il valore di varcontiene meno di due .caratteri);
  • in ksh, con IFS=. some-functiondove some-functionè definito con la sintassi ksh function some-function …;
  • in bash e zsh, IFS=. some-functionpurché operino in modalità nativa anziché in modalità compatibilità.

" IFSrimane impostato su ." Eek. Dopo aver letto la prima parte, questo ha senso, ma prima di pubblicare questa Q, non me lo sarei aspettato.
Muru,

1
Questa è una risposta a una domanda diversa
schily,

Qualche spiegazione aggiuntiva in questa risposta di alcuni anni fa .
Ti Strga,

6

La risposta di @Gilles è davvero eccezionale, spiega (in dettaglio) un problema complesso.

Tuttavia, credo che la risposta al perché questo comando:

$ IFS=. printf "%s\n" $var
a.b.c

funziona così com'è l'idea semplice che l'intera riga di comando venga analizzata prima di essere eseguita. E che ogni "parola" viene elaborata una volta dalla shell.
I compiti, come IFS=., sono in ritardo (il passaggio 4 è l'ultimo):

4.- Ogni assegnazione variabile deve essere ampliata ...

fino a poco prima dell'esecuzione del comando e tutte le espansioni negli argomenti vengono elaborate per prime per creare questa riga eseguibile:

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

Il valore di $varviene espanso con il "vecchio" IFS a.b.cprima che al comando printfvengano dati gli argomenti "%s\n"e a.b.c.

Eval

Un livello di ritardo può essere introdotto da eval:

$ IFS=. eval printf "'%s\n'" \$var
a
b
c

La riga viene analizzata (prima volta) e 'IFS =.' è impostato sull'ambiente come questo:

$ printf '%s\n' $var

Quindi viene analizzato di nuovo a questo:

$ printf '%s\n' a b c

Ed eseguito per questo:

a
b
c

Il valore di $var(abc) è diviso con il valore di IFS in uso: ..

Ambiente

La parte complessa e difficile è ciò che è valido nell'ambiente quando !!!

Ciò è spiegato molto bene nella prima parte della risposta di Gilles.

Con un dettaglio aggiuntivo.

Quando viene eseguito questo comando:

$ IFS=. arr=($var)

Il valore di IFS viene mantenuto nell'ambiente attuale, sì:

$ printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  <.> 

IFS per una singola istruzione.

Ma potrebbe essere evitato: impostare IFS per una singola istruzione

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 

2

La tua domanda in merito

var=a.b.c
IFS=. printf "%s\n" $var

è un caso d'angolo.

Questo perché il macro expansioncomando in accade prima cheIFS=. venga impostata la variabile shell .

In altre parole: quando $varviene espanso, il IFSvalore precedente è attivo, quindi IFSè impostato su '.'.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.