1

Basically, how to achieve this in Bash? Parsing the output of locale - declare "$(locale | grep ^LC_CTYPE | tr --delete '"')" - seems yucky, as it involves four separate commands. Remember, just because locale prints values for most or all variables doesn't mean those variables are actually set! For example, locale prints LC_CTYPE="en_NZ.UTF-8" (among other lines) on my machine, but echo "$LC_CTYPE" prints nothing.

10
  • In a subshell, eval the output of locale and then see $LC_CTYPE? I'm not posting this as an answer because I have not determined that doing this is always safe.
    – Kusalananda
    Commented Sep 28, 2023 at 10:35
  • I don't really want to set the variables if I can avoid it.
    – l0b0
    Commented Sep 28, 2023 at 10:38
  • That's why I mentioned possibly doing it in a subshell.
    – Kusalananda
    Commented Sep 28, 2023 at 10:49
  • What do you actually want though, the value of the $LC_CTYPE variable or the value returned by locale? If the latter, is locale | grep -Po 'LC_CTYPE="\K[^"]+' simple enough or do you need something more portable that doesn't require GNU grep? Do you want the variable to be set after your command?
    – terdon
    Commented Sep 28, 2023 at 10:53
  • 1
    OK, then can you clarify if you want the LC_CTYPE variable to be set after whatever command or if you just want to see the value? Any reason yo don't just do . <(locale | grep '^LC_CTYPE=') for example? Why do you need declare?
    – terdon
    Commented Sep 28, 2023 at 11:04

4 Answers 4

6

Assuming evaluating the output of locale is safe (we assume that locale is not a shell function, alias, or other non-standard utility):

(eval "$(locale)" && printf '%s\n' "$LC_CTYPE")

This sets all the LC_ and associated variables in the subshell environment and outputs the value of the LC_CTYPE variable. Performing the eval inside the subshell avoids polluting the parent environment with the LC_ variables.

Example run on my OpenBSD system where I only ever set LANG=C.UTF-8:

$ (eval "$(locale)" && printf '%s\n' "$LC_CTYPE")
C.UTF-8

If you like to set (and export) the LC_CTYPE variable to the value reported by locale without setting other variables:

$ export LC_CTYPE="$(eval "$(locale)" && printf '%s\n' "$LC_CTYPE")"
$ printf '%s\n' "$LC_CTYPE"
C.UTF-8
0
2

According to man locale the value of LC_CTYPE being enclosed in double quotes signifies it's an "implied value":

Values for variables set in the environment are printed without
double quotes, implied values are printed with double quotes.

which means that LC_CTYPE is not set and locale will print the "implied" value which, if I understand correctly, will be the value of LC_ALL or, if that's not set either, the value of LANG:

For glibc, first (regardless of category), the environment variable LC_ALL is inspected, next the environment variable with the same name as the category, and finally the environment variable LANG. The first existing environment variable is used

So locale will use the first value it finds, in this order: LC_ALL, then LC_CTYPE and then LANG Note that LC_ALL takes precedence if set, so even if LC_CTYPE was set too (maybe to a different locale) locale will report the value of LC_ALL for LC_CTYPE.

In conclusion, to get the value of LC_CTYPE that is currently being used by locale you could run something like

echo ${LC_ALL:-${LC_CTYPE:-$LANG}}

Initially I had thought the goal was to get the actual value of LC_CTYPE if set and only if it's not set, fall back to what locale uses, in which case the first two variables have to be swapped:

echo ${LC_CTYPE:-${LC_ALL:-$LANG}}
3
  • "if I understand correctly, will be the value of LC_ALL or, if that's not set either, the value of LANG." Do you have a reference for that? If that's really the case your solution is the simplest.
    – l0b0
    Commented Sep 28, 2023 at 20:19
  • That reads like the precedence is LC_ALL > all other LC_* variables > LANG. So it should be ${LC_ALL:-${LC_CTYPE:-$LANG}}?
    – l0b0
    Commented Sep 28, 2023 at 22:15
  • 1
    @l0b0 - ok, my bad, I understand now what you meant (I guess my ADHD got the best of me...) I've edited the post. Commented Sep 29, 2023 at 11:44
1

If you just want to parse the output of locale and then set the variable, you can just source it directly like this:

. <(locale | grep '^LC_CTYPE=')

That selects only the LC_TYPE line:

$ locale | grep '^LC_CTYPE='
LC_CTYPE="en_US.UTF-8"

And then you source that, using the . builtin, into the current shell:

$ echo $LC_CTYPE 

$ . <(locale | grep '^LC_CTYPE=')
$ echo "$LC_CTYPE"
en_US.UTF-8

Note that this requires a shell that supports process substitution, such as bash or zsh or ksh and others. If you are not using such a shell, you might need to do something more complicated like

locale | grep '^LC_CTYPE=' > file && . ./file
3
  • The value could just be output, stripping the assignment, and without the need to write it into a file: locale | sed -n '/LC_CTYPE/{s/^LC_CTYPE=//;p}'. Commented Sep 28, 2023 at 13:39
  • @Vilinkameni you mean LC_CTYPE=$(locale | sed -n '/LC_CTYPE/{s/^LC_CTYPE=//;p}')? That won't work, you need to remove the quotes else you get an error (setlocale: LC_CTYPE: cannot change locale ("en_US.UTF-8")). In any case, you wouldn't use something that complicated, even if you had to use sed, you could do locale | sed -n 's/^LC_CTYPE=//p'.
    – terdon
    Commented Sep 28, 2023 at 13:43
  • Of course. If setting the variable is intended, the whole thing can be piped through tr -d '"'. Somehow I like this answer better than using eval though. Commented Sep 28, 2023 at 13:46
0

Without using any external commands, except for locale:

while read -r line; do
    [[ "${line}" =~ ^LC_CTYPE= ]] && {
        export LC_CTYPE="${line#*=}"
        break
    }
done < <(locale)
2
  • You don't want LC_CTYPE*, the OP needs to match just LC_CTYPE and nothing else, so you should even change the regex to ^LC_CTYPE= to avoid matching a variable named LC_CTYPE2 or whatever. Also, you're not setting te variable and I think the OP wants that. Can you explain the benefit of this? It is a tiny bit faster than locale | grep -m1 '^LC_TYPE', but the difference is negligible (average 0.00251495 seconds for the shel loop vs 0.00286379 for grep over 300 repetitions).
    – terdon
    Commented Sep 28, 2023 at 13:26
  • The only benefit I saw was not using external commands. However, I did not take into account the performance difference.
    – qqqq
    Commented Sep 28, 2023 at 13:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .