¿Cuál sería la forma más cercana a una forma portátil de obtener el ancho de visualización (al menos en un terminal (uno que muestre caracteres en el entorno local actual con el ancho correcto)) de una cadena de caracteres de un script de shell.
Estoy interesado principalmente en el ancho de los caracteres que no son de control, pero también son bienvenidas las soluciones que tienen en cuenta los caracteres de control como retroceso, retorno de carro y tabulación horizontal.
En otras palabras, estoy buscando una API de shell alrededor de la wcswidth()
función POSIX.
Ese comando debería devolver:
$ that-command 'unix' # 4 fullwidth characters
8
$ that-command 'Stéphane' # 9 characters, one of which zero-width
8
$ that-command 'もで 諤奯ゞ' # 5 double-width Japanese characters and a space
11
Uno podría usar ksh93
's printf '%<n>Ls'
que tenga en cuenta el ancho de caracteres para el relleno de las <n>
columnas, o el col
comando (con, por ejemplo printf '++%s\b\b--\n' <character> | col -b
) para tratar de derivar eso, hay un perl
módulo Text :: CharWidth al menos, pero hay enfoques más directos o portátiles.
Eso es más o menos un seguimiento de esa otra pregunta que era sobre mostrar texto a la derecha de la pantalla para la que necesitaría tener esa información antes de mostrar el texto.
fuente
Respuestas:
En un emulador de terminal, uno podría usar el informe de posición del cursor para obtener posiciones antes / después, por ejemplo, de
y encuentre qué tan anchos están los caracteres impresos en la terminal. Como se trata de una secuencia de control ECMA-48 (así como VT100) compatible con casi cualquier terminal que pueda utilizar, es bastante portátil.
Para referencia
Finalmente, el emulador de terminal determina el ancho imprimible, debido a estos factores:
wcswidth
solo no dice cómo se manejan los caracteres combinados; POSIX no menciona este aspecto en la descripción de esa función.wcswidth
solo (consulte, por ejemplo, el Capítulo 2. Configuración de Cygwin ).xterm
Por ejemplo, tiene la posibilidad de seleccionar caracteres de doble ancho para las configuraciones que se necesitan.Las llamadas a las API de Shell
wcswidth
son compatibles en diversos grados:Esos son más o menos directos: simulando
wcswidth
en el caso de Perl, llamando al tiempo de ejecución C desde Ruby y Python. Incluso podría usar maldiciones, por ejemplo, de Python (que manejaría la combinación de caracteres):filter
función (para líneas simples)addstr
, verificando el error (en caso de que sea demasiado largo), y luego para la posición finalendwin
(que no debe hacer arefresh
)El uso de maldiciones para la salida (en lugar de enviar la información a un script o llamar directamente
tput
) borraría toda la línea (filter
lo limita a una línea).fuente
wcswidth()
tenga que decir sobre nada.plink
que se estableceTERM=xterm
a pesar de que no responde a ninguna secuencia de control. Pero no uso terminales muy exóticos.fold
aparentemente está especificado para manejar caracteres de varios bytes y ancho extendido . Así es como debería manejar el retroceso: el conteo actual del ancho de línea se reducirá en uno, aunque el conteo nunca será negativo. La utilidad de plegado no debe insertar una <nueva línea> inmediatamente antes o después de cualquier <retroceso>, a menos que el siguiente carácter tenga un ancho mayor que 1 y pueda causar que el ancho de la línea exceda el ancho. tal vezfold -w[num]
ypr +[num]
podría ser unida de alguna manera?Para cadenas de una línea, la implementación de GNU
wc
tiene una opción-L
(aka--max-line-length
) que hace exactamente lo que está buscando (excepto los caracteres de control).fuente
tab
(se supone que la tabulación se detiene cada 8 columnas).wc -L <<< 'unix'
→ 8,wc -L <<< 'Stéphane'
→ 8 ywc -L <<< 'もで 諤奯ゞ'
→ 11. PD ¿Considera que "Stéphane" tiene nueve caracteres, uno de los cuales es de ancho cero? Me parece que hay ocho caracteres, uno de los cuales es de varios bytes.En mi
.profile
, llamo un script para determinar el ancho de una cadena en un terminal. Lo uso cuando inicio sesión en la consola de una máquina en la que no confío en el conjunto del sistemaLC_CTYPE
, o cuando inicio sesión de forma remota y no puedo confiar enLC_CTYPE
que coincida con el lado remoto. Mi script consulta el terminal, en lugar de llamar a cualquier biblioteca, porque ese era el punto principal en mi caso de uso: determinar la codificación del terminal.Esto es frágil de varias maneras:
plink
método, y lo resolví usando elplinkx
método en su lugar ).Esto puede o no coincidir con su caso de uso.
El script devuelve el ancho en su estado de retorno, recortado a 100. Uso de muestra:
fuente
printf "\r%*s\r" $((${#text}+8)) " ";
al final decleanup
(agregar 8 es arbitrario; debe ser lo suficientemente largo para cubrir la salida más amplia de las configuraciones regionales más antiguas pero lo suficientemente estrecho para evitar un ajuste de línea). Esto hace que la prueba sea invisible, aunque también supone que no se ha impreso nada en la línea (lo cual está bien en una~/.profile
)text="Éé"
y luego${#text}
le dará el ancho de la pantalla (obtengo4
en un terminal no unicode y2
en un terminal compatible con unicode). Esto no es cierto para bash.${#text}
no te da el ancho de la pantalla. Le da el número de caracteres en la codificación utilizada por la configuración regional actual. Lo cual es inútil para mi propósito, ya que quiero determinar la codificación del terminal. Es útil si desea el ancho de la pantalla por algún otro motivo, pero no es preciso porque no todos los caracteres tienen una unidad de ancho. Por ejemplo, la combinación de acentos tiene un ancho de 0, y los ideogramas chinos tienen un ancho de 2.Eric Pruitt escribió una implementación impresionante de
wcwidth()
ywcswidth()
en Awk disponible en wcwidth.awk . Proporciona principalmente 4 funciones.donde
wcscolumns()
también tolera caracteres no imprimibles.Abrí un problema preguntando sobre el manejo de TAB, ya que
wcscolumns($'My sign is\t鼠鼠')
debería ser mayor que 14. Actualización: Eric agregó la funciónwcsexpand()
para expandir TAB a espacios:fuente
Para ampliar las sugerencias sobre posibles soluciones usando
col
yksh93
en mi pregunta:Usando el
col
debsdmainutils
Debian (puede que no funcione con otrascol
implementaciones), para obtener el ancho de un solo carácter sin control:Ejemplo:
Extendido para una cadena:
Usando
ksh93
'sprintf '%Ls'
:Usando
perl
'sText::CharWidth
:fuente