пытаюсь использовать printf для декодирования символов юникода, переданных в качестве аргументов

пытаюсь использовать printf для декодирования символов юникода, переданных в качестве аргументов

Я пытаюсь вывести на печать некоторые коды Unicode, которые я передаю следующим образом

echo 0024 0025 | xargs -n1 echo # one code per line
  | xargs printf '\u%s\n'

надеясь получить это

$
%

но вот что я получаю

printf: missing hexadecimal number in escape

После нескольких проб и ошибок у меня на самом деле возникло две небольшие проблемы, одна из которых имеет смысл, а другая кажется полной загадкой.


Проблема 1:

printf '\u%s\n' 0024 0025

дает мне это

-bash: printf: missing unicode digit for \u
\u0024
-bash: printf: missing unicode digit for \u
\u0025

Проблема 2:

> # use built-in for $
> printf '\u0024\n'
$
> # use exe for $
> which printf
/usr/bin/printf
> /usr/bin/printf '\u0024\n'
$
> # now use built-in for %
> printf '\u0025\n'
%
> # but look what happens when we use exe for % !!!!
> /usr/bin/printf '\u0025\n'
/usr/bin/printf: invalid universal character name \u0025

(используя >for $, чтобы вы могли увидеть $в выводе)

По какой-то причине некоторые символы работают с exe-версией, а некоторые — нет, хотя все они работают со встроенной функцией printf.


Итак, вот обходной путь, который работал бы, если бы не проблема №2 (но может быть намного медленнее, чем моя первоначальная идея)

echo 0024 0025 | xargs -n1 echo # one item per line
  | xargs -I {} printf '\u{}\n'

но из-за проблемы №2 это работает лишь наполовину:

$ echo 0024 0025 | xargs -n1 echo | xargs -I {} printf '\u{}\n'
$
printf: invalid universal character name \u0025

($ выводится, но % выдает ошибку)


Итак, я думаю, мои вопросы таковы:

-Есть ли способ заставить printf работать с числовым кодом, чтобы я мог запустить printf один раз, а не один раз для каждого аргумента с помощью -I?

-Что я делаю не так, что printfвстроенный не против, а printfexe не любит, но только для %, а не для $?

решение1

Чтобы избежать проблемы двойного расширения ( \uобрабатывается раньше %s), можно использовать %b, по крайней мере в Bash printf:

printf '%b\n' \\u0024 \\u0025

Вы можете предварительно обработать свои входные данные различными способами:

set 0024 0025
printf '%b\n' "${@/#/\\u}"

Автономный printf,как реализовано в GNU coreutils, имеет следующие ограничения на спецификации символов Unicode:

printfинтерпретирует два синтаксиса символов, представленных в ISO C 99: ' \u' для 16-битных символов Unicode (ISO/IEC 10646), указанных как четыре шестнадцатеричные цифрыхххх, и ' \U' для 32-битных символов Unicode, указанных как восемь шестнадцатеричных цифрххххххх. printfвыводит символы Unicode в соответствии с LC_CTYPEлокалью. Символы Unicode в диапазонах U+0000…U+009F, U+D800…U+DFFF не могут быть указаны этим синтаксисом, за исключением U+0024 ($), U+0040 (@) и U+0060 (`).

Это объясняет, почему нельзя производить продукцию %таким образом.

решение2

Стандартная printfутилита не поддерживает \uxxxxescape-последовательности, см.:https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

Предположение, что это может работать, зависит от расширений, которые могут присутствовать в нескольких реализациях (например, во kshвстроенной), но нельзя ожидать, что они будут поддерживаться в целом. См. printfстандартный документ.

Другая проблема, по-видимому, заключается в вашем предположении, что звонок

printf '\u%s\n' 123

приведет к тому же результату, что и вызов:

printf '\u123\n'

Это не работает, так как printfанализирует строку формата поэлементно и не видит ожидаемую строку формата.

Так что даже если вы используете bashдля выполнения скрипта, вы можете просто ожидать, что \uxxэкранирование обратной косой черты будет расширено, если последуют две шестнадцатеричные цифры, а последовательность экранирования появится буквально в строке формата. Если вы хотите, чтобы 4 шестнадцатеричные цифры были расширены, вам нужно иметь \Uxxxxбуквально в строке формата.

Связанный контент