Estoy escribiendo un script que necesita calcular el número de caracteres en la salida de un comando enun solo paso.
Por ejemplo, el uso del comando readlink -f /etc/fstab
debería devolver 10
porque el resultado de ese comando tiene 10 caracteres.
Esto ya es posible con variables almacenadas usando 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 guardando primero 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 Almquist shell (sh) utilizando únicamente utilidades estándar o integradas.
Respuesta1
Conexpresión GNU:
$ expr length + "$(readlink -f /etc/fstab)"
10
Hay +
una característica especial de GNU expr
para garantizar que el siguiente argumento se trate como una cadena incluso si resulta ser un expr
operador como match
,, ...length
+
Lo anterior eliminará cualquier nueva línea final de salida. Para solucionarlo:
$ expr length + "$(readlink -f /etc/fstab; printf .)" - 2
10
El resultado se restó a2porque la nueva línea final 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 (consultelínea 654)
$ LC_ALL=C.UTF-8 expr length ăaa
4
Entonces, puedes usar:
$ printf "ăaa" | LC_ALL=C.UTF-8 wc -m
3
POSIXLY:
$ expr " $(readlink -f /etc/fstab; printf .)" : ".*" - 3
10
El espacio antes de la sustitución del comando evita que el comando se bloquee con una cadena que comienza con -
, por lo que debemos restar 3.
Respuesta2
No estoy seguro de cómo hacer esto con las funciones integradas del shell (Aunque Gnouc es) pero las herramientas estándar pueden ayudar:
Puedes usar
wc -m
el cual cuenta caracteres. Desafortunadamente, también cuenta la nueva línea final, por lo que primero tendrás que deshacerte de ella:readlink -f /etc/fstab | tr -d '\n' | wc -m
Por supuesto que puedes usar
awk
readlink -f /etc/fstab | awk '{print length($0)}'
O Perl
readlink -f /etc/fstab | perl -lne 'print length'
Respuesta3
Normalmente lo hago así:
$ echo -n "$variable" | wc -m
10
Para hacer comandos lo adaptaría así:
$ echo -n "$(readlink -f /etc/fstab)" | wc -m
10
Este enfoque es similar a lo que estabas haciendo en los 2 pasos, excepto que los combinamos en una sola línea.
Respuesta4
Esto funciona, dash
pero requiere que la var de destino esté definitivamente vacía o no configurada. Es por eso que esto es en realidaddoscomandos: vacío explícitamente $l
en el primero:
l=;printf '%.slen is %d and result is %s\n' \
"${l:=$(readlink -f /etc/fstab)}" "${#l}" "$l"
PRODUCCIÓN
len is 10 and result is /etc/fstab
Eso son todas las funciones integradas del shell, sin incluir, readlink
por supuesto, pero evaluarlo en el shell actual de esa manera implica que debe realizar la tarea antes de obtener len, por lo que silenciamos %.s
el primer argumento en la printf
cadena de formato y simplemente lo agrego nuevamente para el valor literal al final de printf
la lista de argumentos.
Con eval
:
l=$(readlink -f /etc/fstab) eval 'l=${#l}:$l'
printf %s\\n "$l"
PRODUCCIÓN
10:/etc/fstab
Puedes acercarte a lo mismo, pero en lugar de la salida en una variable en el primer comando, la obtienes en la salida estándar:
PS4='${#0}:$0' dash -cx '2>&1' "$(readlink -f /etc/fstab)"
...que escribe...
10:/etc/fstab
... al descriptor de archivo 1 sin asignar ningún valor a ninguna variable en el shell actual.