A cosa serve il C
valore LC_ALL
nei sistemi simili a Unix?
So che forza lo stesso locale per tutti gli aspetti, ma cosa fa C
?
A cosa serve il C
valore LC_ALL
nei sistemi simili a Unix?
So che forza lo stesso locale per tutti gli aspetti, ma cosa fa C
?
Risposte:
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
LC_ALL
è la variabile di ambiente che sovrascrive tutte le altre impostazioni di localizzazione ( tranne $LANGUAGE
in 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 $LANG
tue preferenze con un valore che identifica la tua regione (come fr_CH.UTF-8
se fossi nella Svizzera francese, usando UTF-8). Le singole LC_xxx
variabili hanno la precedenza su un certo aspetto. LC_ALL
li sovrascrive tutti. Il locale
comando, 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 C
locale è 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 a
a z
, è necessario impostare LC_ALL=C
.
Sui sistemi GNU LC_ALL=C
e LC_ALL=POSIX
(o LC_MESSAGES=C|POSIX
) sovrascrivere $LANGUAGE
, mentre LC_ALL=anything-else
no.
Alcuni casi in cui in genere è necessario impostare LC_ALL=C
:
sort -u
oppure sort ... | uniq...
. In molte lingue diverse da C, su alcuni sistemi (in particolare quelli GNU), alcuni caratteri hanno lo stesso ordinamento . sort -u
non 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 C
garantisce il locale).=
operatore conforme a POSIX expr
o l' ==
operatore conforme a POSIX awk
( mawk
e gawk
non sono POSIX al riguardo), che non verificano se due stringhe sono identiche ma se ordinano la stessa.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-Z
caratteri ASCII, è necessario uno LC_ALL=C grep '[[:alpha:]]'
o LC_ALL=C grep '[a-zA-Z]'
¹. [a-z]
corrisponde ai caratteri che ordinano dopo a
e 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 bash
pattern, potrebbero includere [B-Z]
o [A-Y]
. In molti locali UTF-8 (incluso en_US.UTF-8
nella maggior parte dei sistemi), [a-z]
includeranno le lettere latine da a
a y
con segni diacritici ma non quelli di z
(poichéz
prima di loro) che non riesco a immaginare sarebbe quello che vuoi (perché dovresti includere é
e non ź
?).aritmetica in virgola mobile in ksh93
. ksh93
onora l' decimal_point
ambientazione 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.
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 C
locale.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 I
alle 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.big5hkscs
locale (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_ALL
non 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.
C
locale è 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 C
locale. Onestamente non so se questo sia tecnicamente "conforme", ma l'abbiamo visto in libertà .
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.
C
è la locale predefinita, "POSIX" è l'alias di "C". Immagino che "C" sia derivato da ANSI-C. Forse ANSI-C definisce la locale "POSIX".
C
nome della locale derivi da "ANSI C".
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 %b
perché printf \\U25
provoca 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 sort
o 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 sort
o GNU sort
) ma una linea in Ubuntu:
export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
Sembra che LC_COLLATE
controlli 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_COLLATE
per C
realizzare questo. Si noti che ordinerà anche in minuscolo dopo tutte le maiuscole:
A.C
A.H
AFilename.C
a.C
a.H
xclock
warning (Missing charsets in String to FontSet conversion
), sarà meglio se lo useraiLC_ALL=C.UTF-8
per evitare problemi con il cirillico. Per impostare questa variabile d'ambiente è necessario aggiungere la seguente riga alla fine del~/.bashrc
file -export LC_ALL=C.UTF-8