Número de caracteres en la salida de un comando de shell

Número de caracteres en la salida de un comando de shell

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/fstabdebería devolver 10porque 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 exprpara garantizar que el siguiente argumento se trate como una cadena incluso si resulta ser un exproperador 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 readlinky el personaje que .agregamos.

Con la cadena Unicode, exprno 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:

  1. Puedes usar wc -mel 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
    
  2. Por supuesto que puedes usarawk

    readlink -f /etc/fstab | awk '{print length($0)}'
    
  3. 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, dashpero 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 $len 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, readlinkpor supuesto, pero evaluarlo en el shell actual de esa manera implica que debe realizar la tarea antes de obtener len, por lo que silenciamos %.sel primer argumento en la printfcadena de formato y simplemente lo agrego nuevamente para el valor literal al final de printfla 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.

información relacionada