Che cosa fa "LC_ALL = C"?


324

A cosa serve il Cvalore LC_ALLnei sistemi simili a Unix?

So che forza lo stesso locale per tutti gli aspetti, ma cosa fa C?


Se vuoi risolvere un problema con xclockwarning ( Missing charsets in String to FontSet conversion), sarà meglio se lo userai LC_ALL=C.UTF-8per evitare problemi con il cirillico. Per impostare questa variabile d'ambiente è necessario aggiungere la seguente riga alla fine del ~/.bashrcfile -export LC_ALL=C.UTF-8
fedotsoldier

@fedotsoldier probabilmente dovresti porre una domanda e dare tu stesso la risposta, non credo sia correlata alla domanda. È solo una risposta a diversi problemi che stai riscontrando.
jcubic,

Sì, hai ragione, ok
fedotsoldier il

Risposte:


209

Forza le applicazioni a utilizzare la lingua predefinita per l'output:

$ LC_ALL=es_ES man
¿Qué página de manual desea?

$ LC_ALL=C man
What manual page do you want?

e forza l'ordinamento in base al byte:

$ LC_ALL=en_US sort <<< $'a\nb\nA\nB'
a
A
b
B

$ LC_ALL=C sort <<< $'a\nb\nA\nB'
A
B
a
b

20
+1 per buoni esempi, ma manca le informazioni importanti che sono sulla risposta di Stephane ...
Olivier Dulac,

4
Cosa intendi per lingua predefinita ?
Stéphane Chazelas,

2
Sì, capisco che l'autore può fare tutto ciò che gli piace, incluso non fare ciò che dice sulla confezione. La cosa è. L'inglese americano è l'unica lingua che può essere rappresentata correttamente con il set di caratteri in LC_ALL = C, l'unica lingua in cui il criterio di ordinamento in LC_ALL = C (LC_COLLATE) ha senso, LC_ALL = C (LC_TIME) ha nomi di mese e giorno inglesi. Non ho mai visto app in cui LC_ALL = C ha restituito un messaggio in una lingua diversa da LC_ALL = en LANGUAGE = en. Quindi ho il diritto di segnalare un bug a un programma se non è così? (non parlando di app non tradotte in inglese qui).
Stéphane Chazelas,

2
Il problema è "L'inglese americano è l'unica lingua che può essere rappresentata correttamente con il set di caratteri in LC_ALL = C". Questo di solito è vero solo nei programmi C / C ++ quando si usano caratteri stretti, ma anche in questo caso ci sono eccezioni (poiché ci sono diverse lingue che usano solo caratteri e simboli trovati in ASCII). Segnalare un bug quando la lingua predefinita non è l'inglese ti farà sembrare ... bigotto.
Ignacio Vazquez-Abrams,

3
Nota che in inglese (che significa LANG = en_US.utf8) i messaggi possono (e dovrebbero) usare caratteri unicode come “” per citare stringhe. Mentre in LANG = C, ha solo quelli ASCII (virgolette doppie, backquotes e apostrofi).
Ángel

332

LC_ALLè la variabile di ambiente che sovrascrive tutte le altre impostazioni di localizzazione ( tranne $LANGUAGEin alcune circostanze ).

È possibile impostare diversi aspetti delle localizzazioni (come il carattere separatore delle migliaia o il punto decimale, il set di caratteri, l'ordinamento, i nomi di mese, giorno, lingua o applicazione come messaggi di errore, simbolo di valuta) utilizzando alcune variabili di ambiente.

In genere imposterai le $LANGtue preferenze con un valore che identifica la tua regione (come fr_CH.UTF-8se fossi nella Svizzera francese, usando UTF-8). Le singole LC_xxxvariabili hanno la precedenza su un certo aspetto. LC_ALLli sovrascrive tutti. Il localecomando, quando viene chiamato senza argomento, fornisce un riepilogo delle impostazioni correnti.

Ad esempio, su un sistema GNU, ottengo:

$ locale
LANG=en_GB.UTF-8
LANGUAGE=
LC_CTYPE="en_GB.UTF-8"
LC_NUMERIC="en_GB.UTF-8"
LC_TIME="en_GB.UTF-8"
LC_COLLATE="en_GB.UTF-8"
LC_MONETARY="en_GB.UTF-8"
LC_MESSAGES="en_GB.UTF-8"
LC_PAPER="en_GB.UTF-8"
LC_NAME="en_GB.UTF-8"
LC_ADDRESS="en_GB.UTF-8"
LC_TELEPHONE="en_GB.UTF-8"
LC_MEASUREMENT="en_GB.UTF-8"
LC_IDENTIFICATION="en_GB.UTF-8"
LC_ALL=

Posso ignorare una singola impostazione con ad esempio:

$ LC_TIME=fr_FR.UTF-8 date
jeudi 22 août 2013, 10:41:30 (UTC+0100)

O:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

O sovrascrivi tutto con LC_ALL.

$ LC_ALL=C LANG=fr_FR.UTF-8 LC_MESSAGES=fr_FR.UTF-8 cat /
cat: /: Is a directory

In uno script, se si desidera forzare un'impostazione specifica, poiché non si conoscono le impostazioni che l'utente ha imposto (possibilmente anche LC_ALL), l'opzione migliore, più sicura e in genere l'unica è forzare LC_ALL.

La Clocale è una locale speciale che dovrebbe essere la locale più semplice. Si potrebbe anche dire che mentre le altre impostazioni locali sono per gli umani, le impostazioni locali C sono per i computer. Nella locale C, i caratteri sono singoli byte, il set di caratteri è ASCII (beh, non è necessario, ma in pratica sarà nei sistemi che la maggior parte di noi potrà mai usare), l'ordinamento si basa sui valori dei byte, la lingua è solitamente l'inglese americano (anche se per i messaggi dell'applicazione (diversamente da nomi di mese o giorno o messaggi dalle librerie di sistema), è a discrezione dell'autore dell'applicazione) e cose come i simboli di valuta non sono definiti.

Su alcuni sistemi, c'è una differenza con le impostazioni locali POSIX in cui, ad esempio, l'ordinamento per i caratteri non ASCII non è definito.

Generalmente si esegue un comando con LC_ALL = C per evitare che le impostazioni dell'utente interferiscano con lo script. Ad esempio, se si desidera [a-z]abbinare i 26 caratteri ASCII da aa z, è necessario impostare LC_ALL=C.

Sui sistemi GNU LC_ALL=Ce LC_ALL=POSIX(o LC_MESSAGES=C|POSIX) sovrascrivere $LANGUAGE, mentre LC_ALL=anything-elseno.

Alcuni casi in cui in genere è necessario impostare LC_ALL=C:

  • sort -uoppure sort ... | uniq.... In molte lingue diverse da C, su alcuni sistemi (in particolare quelli GNU), alcuni caratteri hanno lo stesso ordinamento . sort -unon riporta righe univoche, ma uno per ogni gruppo di righe con uguale ordinamento. Quindi, se si desidera linee univoche, è necessario un locale in cui i caratteri sono byte e tutti i caratteri hanno un diverso ordinamento (che Cgarantisce il locale).
  • lo stesso vale per l' =operatore conforme a POSIX expro l' ==operatore conforme a POSIX awk( mawke gawknon sono POSIX al riguardo), che non verificano se due stringhe sono identiche ma se ordinano la stessa.
  • I caratteri variano come in grep. Se intendi abbinare una lettera nella lingua dell'utente, usa grep '[[:alpha:]]'e non modificare LC_ALL. Ma se si desidera abbinare i a-zA-Zcaratteri ASCII, è necessario uno LC_ALL=C grep '[[:alpha:]]'o LC_ALL=C grep '[a-zA-Z]'¹. [a-z]corrisponde ai caratteri che ordinano dopo ae prima z(anche se con molte API è più complicato di così). In altri locali, generalmente non sai cosa siano. Ad esempio, alcune impostazioni locali ignorano il caso dell'ordinamento, quindi [a-z]in alcune API come i bashpattern, potrebbero includere [B-Z]o [A-Y]. In molti locali UTF-8 (incluso en_US.UTF-8nella maggior parte dei sistemi), [a-z]includeranno le lettere latine da aa ycon segni diacritici ma non quelli di z(poichézprima di loro) che non riesco a immaginare sarebbe quello che vuoi (perché dovresti includere ée non ź?).
  • aritmetica in virgola mobile in ksh93. ksh93onora l' decimal_pointambientazione LC_NUMERIC. Se scrivi uno script che contiene a=$((1.2/7)), smetterà di funzionare quando eseguito da un utente la cui locale ha la virgola come separatore decimale:

    $ ksh93 -c 'echo $((1.1/2))'
    0.55
    $ LANG=fr_FR.UTF-8  ksh93 -c 'echo $((1.1/2))'
    ksh93: 1.1/2: arithmetic syntax error
    

    Quindi hai bisogno di cose come:

    #! /bin/ksh93 -
    float input="$1" # get it as input from the user in his locale
    float output
    arith() { typeset LC_ALL=C; (($@)); }
    arith output=input/1.2 # use the dot here as it will be interpreted
                           # under LC_ALL=C
    echo "$output" # output in the user's locale
    

    Come nota a margine: il ,separatore decimale è in conflitto con l' ,operatore aritmetico che può causare ancora più confusione.

  • Quando hai bisogno che i caratteri siano byte. Oggi la maggior parte delle versioni locali sono basate su UTF-8, il che significa che i caratteri possono richiedere da 1 a 6 byte. Quando si ha a che fare con dati che devono essere byte, con utilità di testo, è necessario impostare LC_ALL = C. Migliorerà anche significativamente le prestazioni perché l'analisi dei dati UTF-8 ha un costo.
  • un corollario del punto precedente: quando si elabora un testo in cui non si conosce in quale set di caratteri è scritto l'input, ma si può presumere che sia compatibile con ASCII (come praticamente tutti i set di caratteri). Ad esempio, grep '<.*>'per cercare le righe che contengono un <, >pair non funzionerà se ci si trova in una locale UTF-8 e l'input è codificato in un set di caratteri a 8 bit a byte singolo come iso8859-15. Questo perché .è probabile che solo caratteri corrispondenti e caratteri non ASCII in iso8859-15 non formino un carattere valido in UTF-8. D'altra parte, LC_ALL=C grep '<.*>'funzionerà perché qualsiasi valore di byte forma un carattere valido nella Clocale.
  • In qualsiasi momento in cui si elaborano dati di input o dati di output che non sono previsti da / per un essere umano. Se stai parlando con un utente, potresti voler usare la loro convenzione e la loro lingua, ma per esempio, se generi alcuni numeri per alimentare un'altra applicazione che prevede punti decimali in stile inglese o nomi di mesi in inglese, ti consigliamo di impostare LC_ALL = C:

    $ printf '%g\n' 1e-2
    0,01
    $ LC_ALL=C printf '%g\n' 1e-2
    0.01
    $ date +%b
    août
    $ LC_ALL=C date +%b
    Aug
    

    Ciò vale anche per le cose come confronto case insensitive (come in grep -i) e la conversione caso ( awk's toupper(), dd conv=ucase...). Per esempio:

    grep -i i
    

    non è garantito che corrisponda Ialle impostazioni internazionali dell'utente. In alcuni locali turchi, per esempio, non è così come maiuscolo iè İ(notare il punto) non vi e minuscole Iè ı(notare il punto mancante).


¹ A seconda della codifica del testo, questa non è necessariamente la cosa giusta da fare. Questo è valido per i set di caratteri UTF-8 o a byte singolo (come iso-8859-1), ma non necessariamente per i set di caratteri multibyte non UTF-8.

Ad esempio, se ti trovi in ​​una zh_HK.big5hkscslocale (Hong Kong, usando la variante di Hong Kong della codifica dei caratteri cinesi BIG5) e vuoi cercare lettere inglesi in un file codificato in quei set di caratteri, facendo una delle seguenti operazioni:

LC_ALL=C grep '[[:alpha:]]'

o

LC_ALL=C grep '[a-zA-Z]'

sarebbe sbagliato, perché in quel set di caratteri (e molti altri, ma poco usato da quando è uscito UTF-8), molti caratteri contengono byte che corrispondono alla codifica ASCII dei caratteri A-Za-z. Ad esempio, tutti A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(e molti altri) contengono la codifica di A. è 0x96 0x41 ed Aè 0x41 come in ASCII. Quindi il nostro LC_ALL=C grep '[a-zA-Z]'corrisponderebbe su quelle righe che contengono quei caratteri poiché interpreterebbe erroneamente quelle sequenze di byte.

LC_COLLATE=C grep '[A-Za-z]'

funzionerebbe, ma solo se LC_ALLnon diversamente impostato (che avrebbe la precedenza LC_COLLATE). Quindi potresti finire per dover fare:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

se si desidera cercare lettere inglesi in un file codificato nella codifica della locale.


12
+1, è la risposta migliore (per sottolineare l'override, ecc.). Ma manca i (simpatici) esempi della risposta di Ignacio ^^
Olivier Dulac il

1
Un piccolo pignolo: la Clocale è richiesta solo per supportare il "set di caratteri portatile" (ASCII 0-127) e il comportamento per i caratteri> 127 non è tecnicamente specificato . In pratica, la maggior parte dei programmi li tratterà come dati opachi e li passerà come descritto. Ma non tutti: in particolare, Ruby può soffocare sui dati char con byte> 127 se in esecuzione nella Clocale. Onestamente non so se questo sia tecnicamente "conforme", ma l'abbiamo visto in libertà .
Andrew Janke,

2
@AndrewJanke, sì. Si noti che il set di caratteri portatile non implica ASCII né 0-127. Ci sono state molte discussioni sulla mailing list del gruppo di Austin su quali sarebbero le proprietà del set di caratteri locali "C" e il consenso generale (e che sarà chiarito nella prossima specifica) è che quel set di caratteri sarebbe singolo- byte e comprende l'intero intervallo di 8 bit (con le proprietà descritte qui). Nel frattempo, sì, ci può essere qualche divergenza (come bug o perché la specifica non è abbastanza esplicita). In ogni caso LC_ALL = C è il più vicino possibile ottenere un comportamento sano.
Stéphane Chazelas,

1
Un punto di codice Unicode in UTF-8 può avere un massimo di 4 ottetti (o byte), ma alcuni Personaggi richiedono più di un punto di codice, il che può portare a sequenze più lunghe di 6 ottetti.
12431234123412341234123,

1
@ 12431234123412341234123, la codifica originale UTF-8 copre fino a U + 7FFFFFFF (6 byte, e ci sono alcune estensioni per arrivare fino a 13 byte come perl's \x{7FFFFFFFFFFFFFFF}) e mentre la gamma di punti di codice Unicode è stato arbitrariamente limitato a U + 10FFFF (a causa della limitazione del design UTF-16), alcuni strumenti riconoscono / producono ancora caratteri a 6 byte. Questo è ciò che intendevo per caratteri a 6 byte. Nella semantica Unix, un carattere è un punto di codice. I tuoi più di un "personaggio" di punti di codice sono più comunemente indicati come gruppi graphem per chiarire i caratteri.
Stéphane Chazelas,

7

Cè la locale predefinita, "POSIX" è l'alias di "C". Immagino che "C" sia derivato da ANSI-C. Forse ANSI-C definisce la locale "POSIX".


Sia C che UNIX precedono di gran lunga ANSI C.
un CVn il

@ MichaelKjörling: Quindi? Ho visto la documentazione pre-ANSI e non aveva localizzazioni. Internamente presso AT&T Bell Labs, tutti parlavano inglese.
MSalters il

@MSalters Il fatto che la documentazione pre-ANSI per il linguaggio C non menzioni i locali (il che può o meno implicare che il pre-ANSI, C non avesse il concetto di locali; dopo tutto, sono abbastanza sicuro che il linguaggio non lo faccia ancora , ma non è questo il punto) non implica che il Cnome della locale derivi da "ANSI C".
un CVn il

2
@ MichaelKjörling: ti manca il punto. Quando furono introdotti i locali, "C" significava già "ANSI C". Che ciò significhi K&R C in passato è irrilevante.
MSalters il

3

Per quanto ne so, OS X utilizza l'ordine di confronto dei punti di codice in locali UTF-8, quindi è un'eccezione ad alcuni dei punti menzionati nella risposta di Stéphane Chazelas.

Questo stampa 26 in OS X e 310 in Ubuntu:

export LC_ALL=en_US.UTF-8
printf %b $(printf '\\U%08x\\n' $(seq $((0x11)) $((0x10ffff))))|grep -a '[a-z]'|wc -l

Il codice seguente non stampa nulla in OS X, indicando che l'input è ordinato. I sei caratteri surrogati che vengono rimossi causano un errore di sequenza di byte non valido.

export LC_ALL=en_US.UTF-8
for ((i=1;i<=0x1fffff;i++));do
  x=$(printf %04x $i)
  [[ $x = @(000a|d800|db7f|db80|dbff|dc00|dfff) ]]&&continue
  printf %b \\U$x\\n
done|sort -c

Il codice seguente non stampa nulla in OS X, indicando che non ci sono due punti di codice consecutivi (almeno tra U + 000B e U + D7FF) che hanno lo stesso ordine di confronto.

export LC_ALL=en_US.UTF-8
for ((i=0xb;i<=0xd7fe;i++));do
  printf %b $(printf '\\U%08x\\n' $((i+1)) $i)|sort -c 2>/dev/null&&echo $i
done

(Gli esempi sopra riportati usano %bperché printf \\U25provoca un errore in zsh.)

Alcuni caratteri e sequenze di caratteri che hanno lo stesso ordine di confronto nei sistemi GNU non hanno lo stesso ordine di confronto in OS X. Questo stampa ① prima in OS X (usando OS X sorto GNU sort) ma ② prima in Ubuntu:

export LC_ALL=en_US.UTF-8;printf %s\\n ② ①|sort

Questo stampa tre linee in OS X (usando OS X sorto GNU sort) ma una linea in Ubuntu:

export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u

Qualcuno sa perché c'è questa differenza?
1.61803,

3

Sembra che LC_COLLATEcontrolli anche l '"ordine alfabetico" usato da ls. Le impostazioni internazionali statunitensi verranno ordinate come segue:

a.C
aFilename.C
aFilename.H
a.H

praticamente ignorando i periodi. Potresti preferire:

a.C
a.H
aFilename.C
aFilename.H

Certamente. Impostazione LC_COLLATEper Crealizzare questo. Si noti che ordinerà anche in minuscolo dopo tutte le maiuscole:

A.C
A.H
AFilename.C
a.C
a.H
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.