Як ви повторюєте чотиризначний символ Unicode в Bash?


224

Я б хотів додати череп і перекладини Unicode до мого підказки для оболонки (зокрема, "СКУЛЬ І КРОСБОНИ" (U + 2620)), але я не можу розгадати магічну заклик зробити так, щоб ехо випльовувало його чи будь-яке інше, 4-значний символ Unicode. Двозначні цифри легко. Наприклад, echo -e "\ x55",.

Окрім наведених нижче відповідей, слід зазначити, що, очевидно, ваш термінал повинен підтримувати Unicode, щоб вихід був таким, який ви очікуєте. gnome-terminal добре справляється з цим, але він не обов'язково включається за замовчуванням.

У додатку Terminal macOS Перейдіть до Налаштування-> Кодування та виберіть Unicode (UTF-8).


7
Зауважте, що ваш "двозначний коментар" простий (повторюється) " дійсний лише для значень, що знаходяться "\x7F"в локальній точці UTF-8 (що bashтег пропонує ваш) ... шаблони, представлені одним байтом , ніколи не знаходяться в діапазоні \x80-\xFF. Цей діапазон є незаконним в одиночному байті UTF-8 символів. наприклад, значення U+0080\x80\xC2\x80
кодової точки

4
Напр printf "\\u007C\\u001C".
kenorb

NB: для мене gnome-terminal, echo -e '\ufc'не виробляє і, навіть з набором символів кодування в UTF-8. Однак, наприклад urxvt, друкується, наприклад, printf "\\ub07C\\ub01C"як очікувалося (не з або коробкою).
ізоморфізми

@ Peter.O Чому bashтег такий корисний натяк? Чи різні термінали поширені в CJK або ...?
ізоморфізми

1
@ Peter.O zsh, fish, scsh, elvish тощо ... є багато різних оболонок, кожен може обробляти символи unicode, хоча вони хочуть (чи ні). "Баш" дає зрозуміти, що це питання не стосується якоїсь дивної оболонки, яка робить інакше.
masukomi

Відповіді:


237

У UTF-8 це фактично 6 цифр (або 3 байти).

$ printf '\xE2\x98\xA0'

Щоб перевірити, як вона закодована консоллю, скористайтеся hexdump:

$ printf  | hexdump
0000000 98e2 00a0                              
0000003

5
Видобуток шахти " " замість ☠ ... Чому це?
trusktr

8
Це правда. Я виявив, що використовую LANG=Cзамість цього LANG=en_US.UTF-8. Тепер мої термінали в Gnome показують символи належним чином ... Справжні термінали (tty1-6) все ще не знаходяться.
trusktr

6
Для тих, хто намагається зробити hexdump: 0000000 f0 9f 8d baперекладається на \xf0\x9f\x8d\xba. Приклад відлуння: echo -e "\xf0\x9f\x8d\xba".
Блейз

8
Ви також можете використовувати $'...'синтаксис для введення закодованого символу до змінної, не використовуючи $(...)захоплюючу підзарядку, для використання в контекстах, які самі не інтерпретують послідовності втечі:skull=$'\xE2\x98\xA0'
Ендрю Янке

7
Ще одна річ про hexdump: на моїй машині виводиться друга команда у відповіді 0000000 98e2 00a0. Звичайно, 0000000це лише маловажне зміщення, але байти після цього переходять на \xe2\x98\xa0, тому що машина використовує маленький порядок байт-ендіан.
сигалор

98
% echo -e '\u2620'     # \u takes four hexadecimal digits

% echo -e '\U0001f602' # \U takes eight hexadecimal digits
😂

Це працює в Zsh (я перевірив версію 4.3) і в Bash 4.2 або новішій.


16
що просто випльовує \ u2620, коли я це роблю.
masukomi

Для мене також. Яку оболонку ти використовуєш, Джуліано?
Йоахім Зауер

2
Вибачте, забув сказати, що використовую zsh.
Джуліано

32
Підтримка \ u була додана в Bash 4.2.
Лрі

4
НЕ працює для мене, Mac OS 10.14.2, bash (GNU bash, версія 3.2.57 (1) -випуск (x86_64-apple-darwin18)). Він просто роздруковує введення - $ echo -e '\ u2620' <enter> просто роздруковується: \ u2620
Motti Shneor

68

Поки ваші текстові редактори можуть справлятися з Unicode (імовірно, закодованим у UTF-8), ви можете безпосередньо ввести точку коду Unicode.

Наприклад, у текстовому редакторі Vim ви введете режим вставки та натисніть Ctrl+ V+, Uа потім номер кодової точки у вигляді чотиризначного шістнадцяткового числа (якщо необхідно, прокладіть нулі). Отже, ви наберете Ctrl+ V+ U 2 6 2 0. Дивіться: Який найпростіший спосіб вставити символи Unicode в документ?

У терміналі, на якому працює Bash, ви введете CTRL+ SHIFT+ Uі введете шістнадцяткову кодову точку потрібного символу. Під час введення курсор повинен показувати підкреслений u. Перша введена нецифрова цифра закінчує введення та передає символ. Таким чином, ви зможете надрукувати U + 2620 на Bash, використовуючи наступне:

echo CTRL+ SHIFT+U2620ENTERENTER

(Перший вхід закінчується введенням Unicode, а другий виконує echoкоманду.)

Кредит: Попросіть Ubuntu SE


1
Хорошим джерелом для шістнадцяткових кодів є unicodelookup.com/#0x2620/1
RobM

1
Версія vim, яку я використовую (7.2.411 на RHEL 6.3), не відповідає за бажанням, коли між ctrl-v і u є крапка, але працює добре, якщо ця точка опущена.
Кріс Джонсон

@ChrisJohnson: Я видалив період з інструкцій, він не мав на меті натискання клавіші (саме тому він не з'явився з ефектом клавіатури). Вибачте за непорозуміння.
RobM

5
Остерігайтеся: це працює в терміналі, на якому працює Bash, лише якщо ви працюєте в середовищі GTK + , як Gnome.
Н.Р.

1
Ця здатність C-S-u 2 6 2 0є функцією емулятора терміналу, методу введення X (XIM) або подібного. AFAIK, ви не зможете надіслати SHIFTі CTRLтермінальний рівень. Термінал розмовляє лише символами, а не ключовими ключами та кодами клавіш, як ваш X-сервер (також його 7-бітний для всіх намірів і цілей). У цьому світі CTRLмаскується чотири найбільш значущі біти (& 0b00001111), що призводить до
nabin-info

31

Ось повністю внутрішня реалізація Bash, без розгалуження, необмежений розмір символів Unicode.

fast_chr() {
    local __octal
    local __char
    printf -v __octal '%03o' $1
    printf -v __char \\$__octal
    REPLY=$__char
}

function unichr {
    local c=$1    # Ordinal of char
    local l=0    # Byte ctr
    local o=63    # Ceiling
    local p=128    # Accum. bits
    local s=''    # Output string

    (( c < 0x80 )) && { fast_chr "$c"; echo -n "$REPLY"; return; }

    while (( c > o )); do
        fast_chr $(( t = 0x80 | c & 0x3f ))
        s="$REPLY$s"
        (( c >>= 6, l++, p += o+1, o>>=1 ))
    done

    fast_chr $(( t = p | c ))
    echo -n "$REPLY$s"
}

## test harness
for (( i=0x2500; i<0x2600; i++ )); do
    unichr $i
done

Результат:

─━│┃┄┅┆┇┈┉┊┋┌┍┎┏
┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟
┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯
┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿
╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏
═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟
╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯
╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿
▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏
▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟
■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯
▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿
◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●
◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟
◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯
◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿

Мені дуже цікаво міркувати за методом "навколо" та конкретним використанням змінної REPLY. Я припускаю, що ви ознайомилися з джерелом bash або пробіглися через нього чи щось для оптимізації, і я можу бачити, як ваш вибір може бути оптимізуючим, хоча і сильно залежним від перекладача).
nabin-info

14

Просто поставте "☠" у свій сценарій оболонки. У правильній мові та на консолі, що підтримує Unicode, вона буде надрукована добре:

$ echo 

$

Потворним "вирішенням" було б виведення послідовності UTF-8, але це також залежить від використовуваного кодування:

$ echo -e '\xE2\x98\xA0'

$

13

Швидкий однолінійний перетворювач символів UTF-8 у їх 3-байтний формат:

var="$(echo -n '☠' | od -An -tx1)"; printf '\\x%s' ${var^^}; echo

5
Я б не назвав приклад вище швидким (з 11 командами та їх парамами) ... Також він обробляє лише 3-байтні символи UTF-8` (символи UTF-8 можуть бути 1, 2 або 3 байти) ... Це трохи коротше і працює на 1-3 ++++ байти: printf "\\\x%s" $(printf '☠'|xxd -p -c1 -u).... xxd постачається як частина пакету "vim-common"
Peter.O

PS: Я щойно помітив, що наведений вище приклад hexdump / awk - це переміщення послідовності байтів у байт-парі. Це не стосується дампів UTF-8. Це було б відносно, якби це дамп UTF-16LE і хотів вивести кодові точки Unicode , але це не має сенсу, оскільки вхід UTF-8, а вихід точно такий же як вхідний (плюс \ x перед кожним hexdigit -пара)
Пітер.О

7
Символи UTF-8 можуть мати 1 - 4 байтові послідовності
cms

1
грунтуючись на коментарі @ Peter.O, я знаходжу наступне, тоді як більший, досить зручний:hexFromGlyph(){ if [ "$1" == "-n" ]; then outputSeparator=' '; shift; else outputSeparator='\n'; fi for glyph in "$@"; do printf "\\\x%s" $(printf "$glyph"|xxd -p -c1 -u); echo -n -e "$outputSeparator"; done } # usage: $ hexFromGlyph ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF $ hexFromGlyph -n ☠ ✿ \xE2\x98\xA0 \xE2\x9C\xBF
StephaneAG

2
Людина добрий бог. Подумайте: codepoints () { printf 'U+%04x\n' ${@/#/\'} ; } ; codepoints A R ☯ 🕉 z ... насолоджуйтесь 👍
nabin-info

8

Я використовую це:

$ echo -e '\u2620'

Це набагато простіше, ніж пошук у шістнадцятковому представленні ... Я використовую це у своїх скриптах оболонки. Це працює на gnome-term та urxvt AFAIK.


2
@masukomi, якщо ви знаєте, як використовувати варити, ви можете встановити новіший bash і використовувати його. Вищезазначене добре працює на моєму mac-терміналі при використанні оновленого bash.
mcheema

Так, це добре з новішими версіями bash. Рядки підказок Hower, наприклад, $ PS1 не використовують формати ехо-втечі
cms

6

Вам може знадобитися кодувати точку коду як вісімкову, щоб швидке розширення правильно її розшифрувало.

U + 2620, кодований як UTF-8, є E2 98 A0.

Так у Bash,

export PS1="\342\230\240"

зробить вашу оболонку підказками в череп і кістки.


привіт, який код я повинен ввести для "e0 b6 85"? як я можу його знайти?
Udayantha Udy Warnasuriya

просто перетворіть шістнадцяткові (базові 16) числа e0 b6 85 в восьмигранні (база 8) - використовувати калькулятор, мабуть, найпростіший спосіб зробити це
cms

e0 b6 85 hex - 340 266 205 octal
cms

Це спрацювало, велике спасибі! І btw, ви можете знайти восьмеричну версію на цих сторінках: graphemica.com/%E2%9B%B5
Perlnika

6

У bash для друку символу Unicode для виводу використовується \ x, \ u або \ U (перший для двозначної шестизначної, другий для шестизначної шестизначної, третьої для будь-якої довжини)

echo -e '\U1f602'

Я хочу призначити його змінній синтаксису $ '...'

x=$'\U1f602'
echo $x

5

Якщо ви не заперечуєте проти однопластового Perl:

$ perl -CS -E 'say "\x{2620}"'

-CSдозволяє UTF-8 декодування на вході та UTF-8 кодування на виході. -EНаступний аргумент оцінює як Perl із sayввімкненими сучасними функціями . Якщо ви не хочете нового рядка в кінці, використовуйте printзамість say.


5

Будь-яка з цих трьох команд надрукує потрібний символ у консолі, за умови, що консоль приймає символи UTF-8 (більшість поточних):

echo -e "SKULL AND CROSSBONES (U+2620) \U02620"
echo $'SKULL AND CROSSBONES (U+2620) \U02620'
printf "%b" "SKULL AND CROSSBONES (U+2620) \U02620\n"

SKULL AND CROSSBONES (U+2620) 

Після цього ви можете скопіювати та вставити фактичний гліф (зображення, символ) у будь-який (UTF-8) текстовий редактор.

Якщо вам потрібно побачити, як така кодова точка Unicode кодується в UTF-8, використовуйте xxd (набагато краще шестигранний переглядач, ніж od):

echo $'(U+2620) \U02620' | xxd
0000000: 2855 2b32 3632 3029 20e2 98a0 0a         (U+2620) ....

That means that the UTF8 encoding is: e2 98 a0

Або в HEX, щоб уникнути помилок: 0xE2 0x98 0xA0. Тобто значення між пробілом (HEX 20) та лінійною подачею (Hex 0A).

Якщо ви хочете глибоко зануритися в перетворення чисел на символи: подивіться тут, щоб побачити статтю з вікі Грега (BashFAQ) про кодування ASCII на Bash!


re: "Або в HEX, щоб уникнути помилок ..." Навряд чи я думаю, що перетворення символу unicode в якесь двійкове кодування, яке ви виражаєте в шістнадцяткових символах, допомагає уникнути помилок. Використовуючи позначення unicode в "bash", краще уникнути помилок, тобто: "\ uHHHH --- символ Unicode (ISO / IEC 10646), значення якого - шістнадцяткове значення HHHH (одна-чотири шістнадцяткових цифри); \ UHHHHHHHH ---- символу Unicode (ISO / IEC 10646), значення якого - шістнадцяткове значення HHHHHHHH (одна до восьми шістнадцяткових цифр)
Астана,

4

printfВбудований (так само , як Coreutils ' printf) знає керуючу \uпослідовність , яка приймає 4-значні символи Unicode:

   \uHHHH Unicode (ISO/IEC 10646) character with hex value HHHH (4 digits)

Тест з Bash 4.2.37 (1):

$ printf '\u2620\n'

printf - це також вбудована оболонка. Ви, ймовірно, використовуєте bash macOS за замовчуванням (v3). Спробуйте \printfскористатися автономним виконуваним файлом або спробуйте оновити bash
mcint

4

Вибачте за відродження цього старого питання. Але при використанні bashіснує дуже простий підхід до створення точок коду Unicode з простого вводу ASCII, який навіть зовсім не розщедриться :

unicode() { local -n a="$1"; local c; printf -vc '\\U%08x' "$2"; printf -va "$c"; }
unicodes() { local a c; for a; do printf -vc '\\U%08x' "$a"; printf "$c"; done; };

Використовуйте його наступним чином для визначення певних кодових точок

unicode crossbones 0x2620
echo "$crossbones"

або скинути перші 65536 unicode кодові точки для stdout (на моїй машині потрібно менше 2 секунд. Додатковий простір полягає у тому, щоб певні символи не перетікали один в одного через шрифт монопростору оболонки):

for a in {0..65535}; do unicodes "$a"; printf ' '; done

або розповісти трохи дуже типову історію батьків (для цього потрібен Unicode 2010):

unicodes 0x1F6BC 32 43 32 0x1F62D 32 32 43 32 0x1F37C 32 61 32 0x263A 32 32 43 32 0x1F4A9 10

Пояснення:

  • printf '\UXXXXXXXX' виводить будь-який символ Unicode
  • printf '\\U%08x' numberдрукує \UXXXXXXXXз числом, перетвореним у шістнадцятковий, після чого подається іншому, printfщоб фактично роздрукувати символ Unicode
  • printf розпізнає восьмеричні (0окт), шістнадцяткові (0xHEX) і десяткові (0 або числа, починаючи з 1 до 9) як числа, тож ви можете вибрати те, яке представлення найкраще відповідає
  • printf -v var ..збирає висновок printfзмінної без вилки (що надзвичайно прискорює роботу)
  • local variable є, щоб не забруднити глобальний простір імен
  • local -n var=otherпсевдоніми varдо otherтаких, що присвоюють varalters other. Тут є одна цікава частина, яка varє частиною локального простору імен, тоді як otherє частиною глобального простору імен.
    • Зверніть увагу , що не існує такого поняття , як localі globalпростір імен в bash. Змінні зберігаються в навколишньому середовищі, і такі завжди є глобальними. Місцевий просто відкладає поточне значення і відновлює його, коли функція знову залишається. Інші функції, викликані в межах функції з local, все ще побачать значення "локальне". Це принципово інша концепція, ніж всі звичайні правила визначення обсягу, які можна знайти в інших мовах (і що bashдуже важливо, але може призвести до помилок, якщо ви програміст, який цього не знає).

ну - не працює для мене взагалі. будь-яка спроба використовувати будь-яку з ваших функцій, висилає: рядок 6: локальний: -n: недійсний варіант локальний: використання: локальна назва [= значення] ... Я використовую останні (10.14.2) MacOS та bash (GNU bash , версія 3.2.57 (1) -випуск (x86_64-apple-darwin18))
Motti Shneor

4

Ось список усіх доступних смайликів Unicode:

https://en.wikipedia.org/wiki/Emoji#Unicode_blocks

Приклад:

echo -e "\U1F304"
🌄

Для отримання значення ASCII цього символу використовуйте hexdump

echo -e "🌄" | hexdump -C

00000000  f0 9f 8c 84 0a                                    |.....|
00000005

А потім скористайтеся значеннями, повідомленими у шестигранному форматі

echo -e "\xF0\x9F\x8C\x84\x0A"
🌄

відлуння рядка \ U <hex> не працює на OSX, воно просто виводить саме те, що є в лапках.
masukomi


2

Легко за допомогою одного вкладиша Python2 / 3:

$ python -c 'print u"\u2620"'    # python2
$ python3 -c 'print(u"\u2620")'  # python3

Призводить до:


2

На Bash:

UnicodePointToUtf8()
{
    local x="$1"               # ok if '0x2620'
    x=${x/\\u/0x}              # '\u2620' -> '0x2620'
    x=${x/U+/0x}; x=${x/u+/0x} # 'U-2620' -> '0x2620'
    x=$((x)) # from hex to decimal
    local y=$x n=0
    [ $x -ge 0 ] || return 1
    while [ $y -gt 0 ]; do y=$((y>>1)); n=$((n+1)); done
    if [ $n -le 7 ]; then       # 7
        y=$x
    elif [ $n -le 11 ]; then    # 5+6
        y=" $(( ((x>> 6)&0x1F)+0xC0 )) \
            $(( (x&0x3F)+0x80 ))" 
    elif [ $n -le 16 ]; then    # 4+6+6
        y=" $(( ((x>>12)&0x0F)+0xE0 )) \
            $(( ((x>> 6)&0x3F)+0x80 )) \
            $(( (x&0x3F)+0x80 ))"
    else                        # 3+6+6+6
        y=" $(( ((x>>18)&0x07)+0xF0 )) \
            $(( ((x>>12)&0x3F)+0x80 )) \
            $(( ((x>> 6)&0x3F)+0x80 )) \
            $(( (x&0x3F)+0x80 ))"
    fi
    printf -v y '\\x%x' $y
    echo -n -e $y
}

# test
for (( i=0x2500; i<0x2600; i++ )); do
    UnicodePointToUtf8 $i
    [ "$(( i+1 & 0x1f ))" != 0 ] || echo ""
done
x='U+2620'
echo "$x -> $(UnicodePointToUtf8 $x)"

Вихід:

─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟
┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿
╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟
╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿
▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟
■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿
◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟
◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿
U+2620 -> 

0

Якщо значення шістнадцяткових знаків unicode відомо

H="2620"
printf "%b" "\u$H"

Якщо відоме десяткове значення символу унікоду

declare -i U=2*4096+6*256+2*16
printf -vH "%x" $U              # convert to hex
printf "%b" "\u$H"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.