Was macht "LC_ALL = C"?

324

Was macht der CWert für LC_ALLin Unix-ähnlichen Systemen?

Ich weiß, dass es das gleiche Gebietsschema für alle Aspekte erzwingt, aber was macht Cdas?

jcubic
quelle
Wenn Sie ein Problem mit xclockwarning ( Missing charsets in String to FontSet conversion) lösen möchten , ist es besser LC_ALL=C.UTF-8, Probleme mit kyrillischer Sprache zu vermeiden. Um diese Umgebungsvariable festzulegen, müssen Sie die folgende Zeile am Ende der ~/.bashrcDatei hinzufügen -export LC_ALL=C.UTF-8
fedotsoldier
@fedotsoldier Du solltest wahrscheinlich selbst eine Frage stellen und die Antwort geben, ich glaube nicht, dass sie mit der Frage zusammenhängt. Es ist nur eine Antwort auf ein anderes Problem, das Sie haben.
JCUBIC
Ja, du hast recht, ok
Fedotsoldier

Antworten:

209

Es erzwingt, dass Anwendungen die Standardsprache für die Ausgabe verwenden:

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

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

und zwingt die Sortierung, byteweise zu sein:

$ 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
Ignacio Vazquez-Abrams
quelle
20
+1 für gute Beispiele, aber es fehlen die wichtigen Informationen, die auf Stephanes Antwort sind ...
Olivier Dulac
4
Was meinst du mit Standardsprache ?
Stéphane Chazelas
2
Ja, ich verstehe, der Autor kann tun, was er will, auch nicht das, was er verspricht. Die Sache ist. US-Englisch ist die einzige Sprache, die mit dem Zeichensatz in LC_ALL = C korrekt dargestellt werden kann. Dies ist die einzige Sprache, in der die Sortierreihenfolge in LC_ALL = C (LC_COLLATE) sinnvoll ist. LC_ALL = C (LC_TIME) hat englische Monats- und Tagesnamen. Ich habe noch nie Apps gesehen, bei denen LC_ALL = C eine Nachricht in einer anderen Sprache als LC_ALL = en LANGUAGE = en zurückgegeben hat. Darf ich einen Programmfehler melden, wenn dies nicht der Fall ist? (Ich spreche hier nicht von Apps, die nicht ins Englische übersetzt wurden.)
Stéphane Chazelas
2
Das Problem ist "US-Englisch ist die einzige Sprache, die mit dem Zeichensatz in LC_ALL = C korrekt dargestellt werden kann". Dies ist normalerweise nur in C / C ++ - Programmen der Fall, wenn enge Zeichen verwendet werden. Es gibt jedoch auch Ausnahmen (da es mehrere Sprachen gibt, die nur Zeichen und Symbole verwenden, die in ASCII vorkommen). Das Melden eines Fehlers, wenn die Standardsprache nicht Englisch ist, lässt Sie ... bigott erscheinen.
Ignacio Vazquez-Abrams
3
Beachten Sie, dass die Nachrichten in Englisch (dh LANG = en_US.utf8) Unicode-Zeichen wie "" für das Zitieren von Zeichenfolgen verwenden können (und sollten). Während in LANG = C nur ASCII-Zeichen verwendet werden (doppelte Anführungszeichen, Anführungszeichen und Apostrophe).
Ángel
332

LC_ALList die Umgebungsvariable, die alle anderen Lokalisierungseinstellungen überschreibt ( außer $LANGUAGEunter bestimmten Umständen ).

Verschiedene Aspekte von Lokalisierungen (z. B. Tausendertrennzeichen oder Dezimalzeichen, Zeichensatz, Sortierreihenfolge, Monat, Tagesnamen, Sprach- oder Anwendungsnachrichten wie Fehlermeldungen, Währungssymbol) können mithilfe weniger Umgebungsvariablen festgelegt werden.

In der Regel legen Sie $LANGIhre Präferenz mit einem Wert fest, der Ihre Region kennzeichnet (z. B. fr_CH.UTF-8wenn Sie sich in der französischsprachigen Schweiz befinden und UTF-8 verwenden). Die einzelnen LC_xxxVariablen überschreiben einen bestimmten Aspekt. LC_ALLüberschreibt sie alle. Der localeBefehl, wenn ohne Argument aufgerufen gibt einen Überblick über die aktuellen Einstellungen.

Zum Beispiel bekomme ich auf einem GNU-System:

$ 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=

Ich kann eine individuelle Einstellung überschreiben, zum Beispiel mit:

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

Oder:

$ LC_MONETARY=fr_FR.UTF-8 locale currency_symbol
€

Oder alles mit LC_ALL überschreiben.

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

Wenn Sie in einem Skript eine bestimmte Einstellung erzwingen möchten, da Sie nicht wissen, welche Einstellungen der Benutzer erzwungen hat (möglicherweise auch LC_ALL), ist die beste, sicherste und allgemein einzige Option, LC_ALL zu erzwingen.

Das CGebietsschema ist ein spezielles Gebietsschema, das das einfachste Gebietsschema sein soll. Man könnte auch sagen, dass die anderen Ländereinstellungen für Menschen sind, die Ländereinstellung C jedoch für Computer. Im Gebietsschema C sind Zeichen Einzelbytes, der Zeichensatz ist ASCII (nun ja, dies ist nicht erforderlich, wird aber in der Praxis in den Systemen verwendet, die die meisten von uns jemals verwenden werden). Die Sortierreihenfolge basiert auf den Bytewerten. Die Sprache ist in der Regel US-Englisch (bei Anwendungsnachrichten (im Gegensatz zu Monats- oder Tagesnamen oder Nachrichten von Systembibliotheken) liegt es im Ermessen des Anwendungsautors) und Währungssymbole sind nicht definiert.

Auf einigen Systemen gibt es einen Unterschied zum POSIX-Gebietsschema, bei dem beispielsweise die Sortierreihenfolge für Nicht-ASCII-Zeichen nicht definiert ist.

Im Allgemeinen führen Sie einen Befehl mit LC_ALL = C aus, um zu vermeiden, dass die Einstellungen des Benutzers Ihr Skript beeinträchtigen. Wenn Sie beispielsweise [a-z]die 26 ASCII-Zeichen von abis abgleichen möchten z, müssen Sie festlegen LC_ALL=C.

Auf GNU-Systemen LC_ALL=Cund LC_ALL=POSIX(oder LC_MESSAGES=C|POSIX) außer Kraft setzen $LANGUAGE, während LC_ALL=anything-elsenicht.

Einige Fälle, in denen Sie normalerweise festlegen müssen LC_ALL=C:

  • sort -uoder sort ... | uniq.... In vielen anderen Gebietsschemas als C haben auf einigen Systemen (insbesondere GNU-Systemen) einige Zeichen dieselbe Sortierreihenfolge . sort -umeldet keine eindeutigen Zeilen, sondern eine aus jeder Gruppe von Zeilen mit gleicher Sortierreihenfolge. Wenn Sie also eindeutige Zeilen wünschen, benötigen Sie ein Gebietsschema, bei dem die Zeichen Byte sind und alle Zeichen eine unterschiedliche Sortierreihenfolge haben (was das CGebietsschema garantiert).
  • Gleiches gilt für den =Betreiber von POSIX-konformen exproder den ==Betreiber von POSIX-konformen awk( mawkund gawkdiesbezüglich nicht POSIX-konformen ) Zeichenketten, die nicht prüfen, ob zwei Zeichenketten identisch sind, sondern ob sie gleich sortieren.
  • Zeichenbereiche wie in grep. Wenn Sie einen Buchstaben in der Sprache des Benutzers suchen möchten, verwenden Sie grep '[[:alpha:]]'und ändern Sie ihn nicht LC_ALL. Wenn Sie jedoch die a-zA-ZASCII-Zeichen abgleichen möchten , benötigen Sie entweder LC_ALL=C grep '[[:alpha:]]'oder LC_ALL=C grep '[a-zA-Z]'¹. [a-z]stimmt mit den Zeichen überein, die nach aund vor sortiert werden z(obwohl es bei vielen APIs komplizierter ist). In anderen Regionen wissen Sie im Allgemeinen nicht, was das ist. Beispielsweise ignorieren einige Gebietsschemas die Groß- und Kleinschreibung für die Sortierung, so dass [a-z]in einigen APIs wie bashMustern [B-Z]oder enthalten sein können [A-Y]. In vielen UTF-8-Sprachumgebungen (einschließlich der en_US.UTF-8meisten Systeme) [a-z]werden die lateinischen Buchstaben von abis ymit diakritischen Zeichen eingeschlossen, nicht jedoch die von z(seit)zIch kann mir nicht vorstellen, was Sie wollen (warum sollten Sie einbeziehen éund nicht ź?).
  • Gleitkomma-Arithmetik in ksh93. ksh93ehrt die decimal_pointEinstellung in LC_NUMERIC. Wenn Sie ein Skript schreiben, das Folgendes enthält a=$((1.2/7)), funktioniert es nicht mehr, wenn es von einem Benutzer ausgeführt wird, dessen Gebietsschema Komma als Dezimaltrennzeichen hat:

    $ 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
    

    Dann brauchen Sie Dinge wie:

    #! /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
    

    Als Randnotiz: Das ,Dezimaltrennzeichen steht in Konflikt mit dem ,arithmetischen Operator, was noch mehr Verwirrung stiften kann.

  • Wenn Sie Zeichen benötigen, um Bytes zu sein. Heutzutage basieren die meisten Gebietsschemata auf UTF-8, was bedeutet, dass Zeichen 1 bis 6 Byte lang sein können. Wenn Sie mit Daten arbeiten, die Bytes sein sollen, sollten Sie mit Textdienstprogrammen LC_ALL = C setzen. Dies wird auch die Leistung erheblich verbessern, da das Parsen von UTF-8-Daten mit Kosten verbunden ist.
  • eine Folge des vorherigen Punktes: Wenn Sie Text verarbeiten, bei dem Sie nicht wissen, in welchem ​​Zeichensatz die Eingabe geschrieben ist, aber davon ausgehen können, dass er mit ASCII kompatibel ist (wie praktisch alle Zeichensätze). Wenn Sie beispielsweise grep '<.*>'nach Zeilen suchen, die ein enthalten <, >funktioniert pair nicht, wenn Sie sich in einem UTF-8-Gebietsschema befinden und die Eingabe in einem Einzelbyte-8-Bit-Zeichensatz wie iso8859-15 codiert ist. .Dies liegt daran, dass nur Übereinstimmungen und Nicht-ASCII-Zeichen in ISO8859-15 wahrscheinlich kein gültiges Zeichen in UTF-8 bilden. Funktioniert jedoch, LC_ALL=C grep '<.*>'da jeder Byte-Wert im CGebietsschema ein gültiges Zeichen bildet .
  • Jedes Mal, wenn Sie Eingabedaten oder Ausgabedaten verarbeiten, die nicht für einen Menschen bestimmt sind. Wenn Sie mit einem Benutzer sprechen, möchten Sie möglicherweise dessen Konvention und Sprache verwenden. Wenn Sie jedoch beispielsweise Zahlen generieren, um eine andere Anwendung zu unterstützen, die englische Dezimalstellen oder englische Monatsnamen erwartet, möchten Sie dies setze 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
    

    Das gilt auch für Dinge wie case insensitive compare (wie in grep -i) und case conversion ( awk's toupper(), dd conv=ucase...). Zum Beispiel:

    grep -i i
    

    Es wird nicht garantiert, dass die Übereinstimmung Iim Gebietsschema des Benutzers vorliegt. In einigen türkischen Gegenden zum Beispiel tut es nicht als Großbuchstabe iist İ( man beachte den Punkt) gibt und Klein Iist ı( man beachte den fehlenden Punkt).


¹ Abhängig von der Kodierung des Textes ist dies jedoch nicht unbedingt das Richtige. Dies gilt für UTF-8- oder Einzelbyte-Zeichensätze (wie iso-8859-1), jedoch nicht unbedingt für Nicht-UTF-8-Multibyte-Zeichensätze.

Wenn Sie sich beispielsweise in einem zh_HK.big5hkscsGebietsschema befinden (Hongkong mit der Hongkong-Variante der chinesischen BIG5-Zeichencodierung) und in einer in diesen Zeichensätzen codierten Datei nach englischen Buchstaben suchen möchten, gehen Sie folgendermaßen vor:

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

oder

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

wäre falsch, da in diesem Zeichensatz (und vielen anderen, die seit dem Erscheinen von UTF-8 kaum mehr verwendet wurden) viele Zeichen Bytes enthalten , die der ASCII-Codierung von A-Za-Z-Zeichen entsprechen. Beispielsweise enthalten alle A䨝䰲丕乙乜你再劀劈呸哻唥唧噀噦嚳坽(und viele weitere) die Codierung von A. ist 0x96 0x41 und Aist 0x41 wie in ASCII. Also LC_ALL=C grep '[a-zA-Z]'würden wir in den Zeilen, die diese Zeichen enthalten, übereinstimmen, da dies die Folgen von Bytes falsch interpretieren würde.

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

würde funktionieren, aber nur wenn LC_ALLnichts anderes eingestellt ist (was übersteuern würde LC_COLLATE). Sie müssen also möglicherweise Folgendes tun:

grep '[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz]'

Wenn Sie nach englischen Buchstaben in einer Datei suchen möchten, die in der Codierung des Gebietsschemas codiert ist.

Stéphane Chazelas
quelle
12
+1, das ist die beste Antwort (um auf das Übergeordnete hinzuweisen usw.). Aber es fehlen die (netten) Beispiele für Ignacios Antwort ^^
Olivier Dulac
1
Ein kleiner Trottel: Das CGebietsschema wird nur benötigt, um den "portablen Zeichensatz" (ASCII 0-127) zu unterstützen, und das Verhalten für Zeichen> 127 ist technisch nicht spezifiziert . In der Praxis behandeln die meisten Programme sie als undurchsichtige Daten und leiten sie wie beschrieben weiter. Aber nicht alle: Insbesondere kann Ruby char-Daten mit Bytes> 127 verschlucken, wenn es in der CLändereinstellung ausgeführt wird. Ich weiß ehrlich gesagt nicht, ob das technisch "konform" ist, aber wir haben es in der Natur gesehen .
Andrew Janke
2
@ AndrewJanke, ja. Beachten Sie, dass der portable Zeichensatz weder ASCII noch 0-127 impliziert. In der Mailingliste der Austin-Gruppe wurde viel darüber diskutiert, welche Eigenschaften der Zeichensatz des Gebietsschemas "C" haben soll, und es herrscht allgemeiner Konsens (der in der nächsten Spezifikation geklärt wird), dass dieser Zeichensatz ein einziger Zeichensatz ist. Byte und umfassen den gesamten 8-Bit-Bereich (mit den hier beschriebenen Eigenschaften). In der Zwischenzeit kann es ja zu Abweichungen kommen (als Fehler oder weil die Spezifikation nicht explizit genug ist). In jedem Fall ist LC_ALL = C das vernünftigste Verhalten.
Stéphane Chazelas
1
Ein Unicode-Codepunkt in UTF-8 kann maximal 4 Oktette (oder Bytes) enthalten. Einige Zeichen benötigen jedoch mehr als einen Codepunkt, was zu längeren Sequenzen als 6 Oktette führen kann.
12431234123412341234123
1
@ 12431234123412341234123, die ursprüngliche UTF-8-Codierung deckt bis zu U + 7FFFFFFF (6 Bytes, und es gibt einige Erweiterungen für bis zu 13 Bytes wie perl's \x{7FFFFFFFFFFFFFFF}), und während der Bereich der Unicode-Codepunkte willkürlich auf U + 10FFFF beschränkt wurde (Aufgrund der UTF-16-Designbeschränkung) erkennen / produzieren einige Tools immer noch 6-Byte-Zeichen. Das habe ich mit 6-Byte-Zeichen gemeint. In der Unix-Semantik ist ein Zeichen ein Codepunkt. Ihre mehr als einen Codepunkt "Zeichen" werden allgemeiner als Graphem-Cluster bezeichnet, um sie von Zeichen zu unterscheiden.
Stéphane Chazelas
7

Cist das Standardgebietsschema, "POSIX" ist der Alias ​​von "C". Ich denke, "C" ist von ANSI-C abgeleitet. Möglicherweise definiert ANSI-C das Gebietsschema "POSIX".

Edward Shen
quelle
Sowohl C als auch UNIX sind mit Abstand älter als ANSI C.
ein CVn
@ MichaelKjörling: Also? Ich habe eine Pre-ANSI-Dokumentation gesehen und es gab keine Gebietsschemas. Intern bei AT & T Bell Labs sprachen alle Englisch.
MSalters
@MSalters Die Tatsache , dass prä-ANSI - Dokumentation für die C - Sprache nicht locales nicht erwähnt (was nicht so vorge ANSI kann oder kann bedeuten, C kein Konzept von Schauplätzen hatte, nach allem, ich bin ziemlich sicher , die Sprache noch nicht Dies bedeutet jedoch nicht, dass der CName des Gebietsschemas von "ANSI C" abgeleitet ist.
ein CVn
2
@ MichaelKjörling: Du verpasst den Punkt. Bei der Einführung von Gebietsschemata bedeutete "C" bereits "ANSI C". Dass es in der Vergangenheit K & R C bedeutete, ist irrelevant.
MSalters
3

Soweit ich das beurteilen kann, verwendet OS X die Codepunkt-Sortierreihenfolge in UTF-8-Gebietsschemata, so dass dies eine Ausnahme zu einigen der in der Antwort von Stéphane Chazelas genannten Punkten darstellt.

Dies druckt 26 in OS X und 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

Der folgende Code gibt in OS X nichts aus, was darauf hinweist, dass die Eingabe sortiert ist. Die sechs entfernten Ersatzzeichen verursachen einen unzulässigen Bytefolgenfehler.

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

Der folgende Code gibt in OS X nichts aus, was darauf hinweist, dass es keine zwei aufeinander folgenden Codepunkte (mindestens zwischen U + 000B und U + D7FF) mit derselben Sortierreihenfolge gibt.

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

(Die obigen Beispiele werden verwendet, %bda dies printf \\U25zu einem Fehler in zsh führt.)

Einige Zeichen und Zeichenfolgen, die in GNU-Systemen dieselbe Sortierreihenfolge haben, haben in OS X nicht dieselbe Sortierreihenfolge. Dies druckt ① zuerst in OS X (unter Verwendung von OS X sortoder GNU sort), aber but zuerst in Ubuntu:

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

Dies druckt drei Zeilen in OS X (entweder mit OS X sortoder GNU sort), aber eine Zeile in Ubuntu:

export LC_ALL=en_US.UTF-8;printf %b\\n \\u0d4c \\u0d57 \\u0d46\\u0d57|sort -u
Nisetama
quelle
Weiß jemand, warum es diesen Unterschied gibt?
1.61803
3

Es scheint, dass es LC_COLLATEdie "alphabetische Reihenfolge" steuert, die auch von ls verwendet wird. Das US-Gebietsschema wird wie folgt sortiert:

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

im Grunde ignorieren die Perioden. Vielleicht bevorzugen Sie:

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

Das tue ich sicher. Einstellen LC_COLLATE, Cum dies zu erreichen. Beachten Sie, dass auch Kleinbuchstaben nach allen Großbuchstaben sortiert werden:

A.C
A.H
AFilename.C
a.C
a.H
SteveInCO
quelle