Estoy escribiendo un script que necesita calcular el número de caracteres en la salida de un comando en un solo paso .
Por ejemplo, el uso del comando readlink -f /etc/fstab
debería regresar 10
porque la salida de ese comando tiene 10 caracteres de longitud.
Esto ya es posible con variables almacenadas utilizando el siguiente código:
variable="somestring";
echo ${#variable};
# 10
Desafortunadamente, usar la misma fórmula con una cadena generada por comando no funciona:
${#(readlink -f /etc/fstab)};
# bash: ${#(readlink -f /etc/fstab)}: bad substitution
Entiendo que es posible hacer esto primero guardando el resultado en una variable:
variable=$(readlink -f /etc/fstab);
echo ${#variable};
Pero me gustaría eliminar el paso adicional.
es posible? Es preferible la compatibilidad con el shell Almquist (sh) utilizando solo utilidades incorporadas o estándar.
readlink -f /etc/fstab
es de 11 caracteres. No olvides la nueva línea. De lo contrario, verías/etc/fstabluser@cern:~$
cuándo lo ejecutaste desde un caparazón.Respuestas:
Con GNU expr :
El
+
no es una característica especial de GNUexpr
para asegurarse de que el siguiente argumento es tratado como una cadena, incluso si pasa a ser unexpr
operador comomatch
,length
,+
...Lo anterior eliminará cualquier nueva línea final de salida. Para solucionarlo:
El resultado fue restado a 2 porque la nueva línea final de
readlink
y el personaje.
que agregamos.Con la cadena Unicode,
expr
no parece funcionar, porque devuelve la longitud de la cadena en bytes en lugar del recuento de caracteres (consulte la línea 654 )Entonces, puedes usar:
POSIXLY:
El espacio antes de la sustitución del comando evita que el comando se bloquee con el inicio de la cadena
-
, por lo que debemos restar 3.fuente
LC_ALL=C.UTF-8
, lo que simplifica significativamente las cosas si no se conoce de antemano la codificación de la cadena.expr length $(echo "*")
- no Al menos utilizar comillas dobles:expr length "$(…)"
. Pero esto elimina las nuevas líneas del comando, es una característica ineludible de la sustitución de comandos. (Puede trabajar alrededor de ella, pero entonces la respuesta se vuelve aún más complejo.)No estoy seguro de cómo hacer esto con shell incorporado ( aunque Gnouc sí lo es ), pero las herramientas estándar pueden ayudar:
Puedes usar lo
wc -m
que cuenta caracteres. Desafortunadamente, también cuenta la nueva línea final, por lo que primero debería deshacerse de eso:Por supuesto, puedes usar
awk
O Perl
fuente
expr
es un incorporado? ¿En que caparazón?Usualmente lo hago así:
Para hacer comandos, lo adaptaría así:
Este enfoque es similar a lo que estaba haciendo en sus 2 pasos, excepto que los estamos combinando en un solo revestimiento.
fuente
-m
lugar de-c
. Con los caracteres Unicode, su enfoque se romperá.readlink -f /etc/fstab | wc -m
?${#variable}
? Al menos use comillas doblesecho -n "$variable"
, pero esto todavía falla si, por ejemplo, el valor devariable
es-e
. Cuando lo use en combinación con una sustitución de comando, tenga en cuenta que las nuevas líneas finales se eliminan.Puede llamar a utilidades externas (vea otras respuestas), pero harán que su secuencia de comandos sea más lenta y es difícil hacer la instalación correcta.
Zsh
En zsh, puede escribir
${#$(readlink -f /etc/fstab)}
para obtener la duración de la sustitución del comando. Tenga en cuenta que esta no es la longitud de la salida del comando, es la longitud de la salida sin ninguna nueva línea final.Si desea la longitud exacta de la salida, muestre un carácter adicional que no sea de nueva línea al final y reste uno.
Si lo que desea es la carga útil en la salida del comando, entonces debe restar dos aquí, porque la salida de
readlink -f
es la ruta canónica más una nueva línea.Esto difiere del
${#$(readlink -f /etc/fstab)}
caso raro pero posible en el que el camino canónico termina en una nueva línea.Para este ejemplo específico, no necesita una utilidad externa en absoluto, porque zsh tiene una construcción incorporada que es equivalente a
readlink -f
través del modificador de historialA
.Para obtener la longitud, use el modificador de historial en una expansión de parámetros:
Si tiene el nombre del archivo en una variable
filename
, sería${#filename:A}
.Conchas de estilo Bourne / POSIX
Ninguno de los shells Bourne / POSIX puros (Bourne, ash, mksh, ksh93, bash, yash ...) tiene una extensión similar que yo sepa. Si necesita aplicar una sustitución de parámetros a la salida de una sustitución de comando o anidar sustituciones de parámetros, utilice etapas sucesivas.
Puede rellenar el procesamiento en una función si lo desea.
o
pero generalmente no hay beneficio; excepto con ksh93, eso hace que una bifurcación adicional pueda usar la salida de la función, por lo que hace que su script sea más lento y rara vez hay algún beneficio de legibilidad.
Una vez más, la salida de
readlink -f
es la ruta canónica más una nueva línea; si desea la longitud del camino canónico, reste 2 en lugar de 1 pulgcommand_output_length
. El usocommand_output_length_sans_trailing_newlines
da el resultado correcto solo cuando la ruta canónica en sí misma no termina en una nueva línea.Bytes vs caracteres
${#…}
se supone que es la longitud en caracteres, no en bytes, lo que hace la diferencia en configuraciones regionales multibyte. Las versiones razonablemente actualizadas de ksh93, bash y zsh calculan la longitud en caracteres de acuerdo con el valor deLC_CTYPE
en el momento en que${#…}
se expande la construcción. Muchos otros shells comunes realmente no admiten configuraciones regionales multibyte: a partir del guión 0.5.7, mksh 46 y posh 0.12.3,${#…}
devuelve la longitud en bytes. Si desea la longitud en caracteres de manera confiable, use lawc
utilidad:Siempre que
$LC_CTYPE
designe una configuración regional válida, puede estar seguro de que esto se eliminará por error (en una plataforma antigua o restringida que no admite configuraciones regionales de varios bytes) o devolverá la longitud correcta en caracteres. (Para Unicode, "longitud en caracteres" significa la cantidad de puntos de código; la cantidad de glifos es otra historia, debido a complicaciones como la combinación de caracteres).Si desea la longitud en bytes, establezca
LC_CTYPE=C
temporalmente o use enwc -c
lugar dewc -m
.El conteo de bytes o caracteres
wc
incluye cualquier nueva línea final del comando. Si desea la longitud de la ruta canónica en bytes, esPara obtenerlo en caracteres, resta 2.
fuente
echo .
agrega dos caracteres, pero el segundo carácter es una nueva línea final que es eliminada por la sustitución del comando.readlink
salida, más el.
byecho
. Ambos estamos de acuerdo en queecho .
agreguemos dos caracteres, pero la nueva línea final se eliminó. Pruebeprintf .
o vea mi respuesta unix.stackexchange.com/a/160499/38906 .readlink
es el objetivo del enlace más una nueva línea.Esto funciona
dash
pero requiere que la var objetivo esté definitivamente vacía o desarmada. Es por eso que en realidad son dos comandos: explícitamente vacío$l
en el primero:SALIDA
Eso es todo integrado en el shell, sin incluir el
readlink
supuesto, pero evaluarlo en el shell actual de esa manera implica que debe hacer la asignación antes de obtener el len, razón por la cual hago%.s
ilegibilidad al primer argumento en laprintf
cadena de formato y solo lo agrego nuevamente para El valor literal en la cola deprintf
la lista de argumentos.Con
eval
:SALIDA
Puede acercarse a esa misma cosa, pero en lugar de la salida en una variable en el primer comando, lo obtiene en stdout:
... que escribe ...
... al archivo descriptor 1 sin asignar ningún valor a ningún vars en el shell actual.
fuente
variable=$(readlink -f /etc/fstab); echo ${#variable};
pero me gustaría eliminar el paso adicional".expr
, por ejemplo. Probablemente solo importa si de alguna manera obtener el len ocluye el valor, lo cual admito, estoy teniendo dificultades para entender por qué puede ser, pero sospecho que podría haber un caso en el que importara.eval
forma, por cierto, es probablemente la más limpia aquí: asigna la salida y el len al mismo nombre var en una sola ejecución, muy cerca de hacerlol=length(l):out(l)
. Haciendoexpr length $(command)
hace ocluir el valor a favor de la len, por cierto.