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/fstab
deve retornar 10
porque 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 expr
para garantir que o próximo argumento seja tratado como uma string, mesmo que seja um expr
operador 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 readlink
e o caractere .
que adicionamos.
Com string Unicode, expr
parece 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:
Você pode usar
wc -m
which 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
Claro que você pode usar
awk
readlink -f /etc/fstab | awk '{print length($0)}'
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, dash
mas exige que o var de destino esteja definitivamente vazio ou não definido. É por isso que isso é realmentedoiscomandos - esvazio explicitamente $l
no 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 %.s
o primeiro argumento na printf
string de formato e apenas o adiciono novamente para o valor literal no final da printf
lista 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.