Número de caracteres na saída de um comando shell

Número de caracteres na saída de um comando shell

Estou escrevendo um script que precisa calcular o número de caracteres na saída de um comando emum único passo.

Por exemplo, usar o comando readlink -f /etc/fstabdeve retornar 10porque a saída desse comando tem 10 caracteres.

Isso já é possível com variáveis ​​armazenadas usando o seguinte código:

variable="somestring";
echo ${#variable};
# 10

Infelizmente, usar a mesma fórmula com uma string gerada por comando não funciona:

${#(readlink -f /etc/fstab)};
# bash: ${#(readlink -f /etc/fstab)}: bad substitution

Entendo que é possível fazer isso primeiro salvando a saída em uma variável:

variable=$(readlink -f /etc/fstab);
echo ${#variable};

Mas eu gostaria de remover a etapa extra.

Isso é possível? É preferível a compatibilidade com o shell Almquist (sh) usando apenas utilitários integrados ou padrão.

Responder1

ComExpressão GNU:

$ expr length + "$(readlink -f /etc/fstab)"
10

Existe +um recurso especial do GNU exprpara garantir que o próximo argumento seja tratado como uma string, mesmo que seja um exproperador como match, length, +...

O procedimento acima removerá qualquer nova linha de saída final. Para contornar isso:

$ expr length + "$(readlink -f /etc/fstab; printf .)" - 2
10

O resultado foi subtraído para2porque a nova linha final readlinke o caractere .que adicionamos.

Com string Unicode, exprparece não funcionar, porque retorna o comprimento da string em bytes em vez da contagem de caracteres (vejalinha 654)

$ LC_ALL=C.UTF-8 expr length ăaa
4

Então, você pode usar:

$ printf "ăaa" | LC_ALL=C.UTF-8 wc -m
3

POSIXMENTE:

$ expr " $(readlink -f /etc/fstab; printf .)" : ".*" - 3
10

O espaço antes da substituição do comando evita que o comando trave com a string start with -, então precisamos subtrair 3.

Responder2

Não tenho certeza de como fazer isso com os recursos internos do shell (Gnouc é embora), mas as ferramentas padrão podem ajudar:

  1. Você pode usar wc -mwhich conta caracteres. Infelizmente, ele também conta a nova linha final, então você terá que se livrar dela primeiro:

    readlink -f /etc/fstab | tr -d '\n' | wc -m
    
  2. Claro que você pode usarawk

    readlink -f /etc/fstab | awk '{print length($0)}'
    
  3. Ou Perl

    readlink -f /etc/fstab | perl -lne 'print length'
    

Responder3

Eu costumo fazer assim:

$ echo -n "$variable" | wc -m
10

Para fazer comandos eu adaptaria assim:

$ echo -n "$(readlink -f /etc/fstab)" | wc -m
10

Essa abordagem é semelhante ao que você estava fazendo nas duas etapas, exceto que as estamos combinando em uma única linha.

Responder4

Isso funciona, dashmas exige que o var de destino esteja definitivamente vazio ou não definido. É por isso que isso é realmentedoiscomandos - esvazio explicitamente $lno primeiro:

l=;printf '%.slen is %d and result is %s\n' \
    "${l:=$(readlink -f /etc/fstab)}" "${#l}" "$l"

SAÍDA

len is 10 and result is /etc/fstab

Isso é tudo embutido no shell - sem incluir o, readlinké claro - mas avaliá-lo no shell atual dessa forma implica que você deve fazer a atribuição antes de obter o len, e é por isso que ilencio %.so primeiro argumento na printfstring de formato e apenas o adiciono novamente para o valor literal no final da printflista de argumentos de.

Com eval:

l=$(readlink -f /etc/fstab) eval 'l=${#l}:$l'
printf %s\\n "$l"

SAÍDA

10:/etc/fstab

Você pode chegar perto da mesma coisa, mas em vez da saída em uma variável no primeiro comando você a obtém no stdout:

PS4='${#0}:$0' dash -cx '2>&1' "$(readlink -f /etc/fstab)"

...que escreve...

10:/etc/fstab

...para o descritor de arquivo 1 sem atribuir nenhum valor a nenhum vars no shell atual.

informação relacionada