Capturando o conteúdo do loop em uma variável

2

Eu tenho um loop for que gera tudo, desde /etc/trueuserowners até du . Eu estou tentando descobrir como obter a saída deste for loop e colocar tudo em uma variável sem ajustá-lo em uma matriz.

for i in $(grep dook /etc/trueuserowners | cut -d : -f 1); do du -sh /home/$i; done | sort -nr

O propósito principal disso é que eu possa pegar a saída do du -sk , colocar na variável p e tirar p para calcular os formulários legíveis e obter um total no final.

Eu estou tentando manter isso em um forro, mas posso / vou converter para script bash se eu precisar ABSOLUTAMENTE.

Isso está sendo executado em um servidor com usuários do cPanel.

O conteúdo de /etc/trueuserowners é semelhante a este.

user1: reseller
user2: reseller
user3: reseller

Este seria o exemplo de saída do que eu estou usando seria semelhante ao seguinte.

for i in $(grep root /etc/trueuserowners | cut -d : -f 1); do du -sh /home/$i; done | sort -nr
992K    /home/demoabusecenter
820K    /home/lakshdljkashdlkj
151M    /home/hyg8
58M /home/bugsabusecenter
21G /home/esportsoverlay
3.3G    /home/yourabuse
2.8M    /home/kf30ls08f2k0
1.4M    /home/perm2term
1.2M    /home/justcheck
1.1M    /home/myinfo1

O objetivo geral aqui é fazer com que ele forneça a saída como está agora e forneça o total de cada usuário.

Acho que tenho uma solução para isso, mas não consigo descobrir como colocar essa saída em uma variável.

Isso é o que acontece quando eu tento.

for i in $(grep root /etc/trueuserowners | cut -d : -f 1); do p=$(du -sh /home/$i); done | sort -nr | echo $p
20981044 /home/esportsoverlay

Por alguma razão, converte a saída para espaço em MB. O que é bom porque eu posso converter isso para GB. Eu simplesmente não consigo mostrar tudo. Isso só mostra a última saída do usuário do loop for.

    
por TheDocs 01.12.2017 / 22:41

1 resposta

1

A resposta vai ser um pouco longa, mas eu tenho que abordar algumas partes, então fique comigo aqui.

Bem, vamos começar com o fato de que sua abordagem para iterar a saída grep não está correta. A abordagem for i in $(); do ...done rompe com linhas que contêm espaços iniciais (devido à divisão de palavras ) e geralmente não é recomendado (consulte < a href="http://mywiki.wooledge.org/DontReadLinesWithFor"> isto ).

No entanto, o que normalmente é feito é command | while IFS=<word separator> read -r variable; do...done (que, como bônus adicional, é portável entre os shells parecidos com Bourne). Além disso, vejo que você está usando cut -d : -f 1 para obter o primeiro item da lista separada por dois pontos em cada linha. Podemos usar IFS=":" para nos livrarmos de cut part. Seu comando original é assim transformado assim:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else 
do 
    du -sh /home/$i
done | sort -nr

A variável first_word obviamente conterá apenas o primeiro campo, e o everything_else não utilizado conterá ... todo o resto.

Note que também citei 'dook' part; embora grep entenda o primeiro item. Embora grep entenda o primeiro item não-opcional como PATTERN, é uma boa prática proteger o padrão com aspas simples, porque se você usar * ou alguns outros padrões de regex usados pelo shell, isso ocorrerá sem intenção expansão por bash e tentará ler arquivos em seu diretório de trabalho atual. Veja este para um exemplo detalhado.

Em seguida, vamos abordar as matrizes. Certamente, podemos adicionar novo item aos arrays com o loop while mostrado acima e += operator:

$ declare -a my_array
$ while IFS=":" read -r name null; do my_array+=("$name"); done < /etc/passwd
$ echo "${my_array[0]}"
root

Isso pode ser conveniente se você quiser processar os itens que você está extraindo mais tarde. No entanto, considerando o fato de que você tem pipe lá, as variáveis desaparecem uma vez subshell em que while é executado sai. Veja minha pergunta antiga sobre isso.

O que eu recomendo é processar tudo no while loop e usar du -sb para precisão nos cálculos. Assim, você poderia fazer:

# read what grep finds line by line and print total once there's no more lines from grep
grep 'dook' /etc/trueuserowners | while IFS=":" read -r first_word everything_else || { echo "$total"; break;} 
do 
    user_usage=$( du -sb /home/"$first_word" | awk '{print }' )
    # output the usage for current user
    printf "%s\t/home/%s\n" "$user_usage"  "$first_word"
    total=$(($total+$user_usage))
done 

Observe como usei || { echo "$total"; break;} . Uma vez que não há nada para ler a partir de stdin (que neste caso vem do pipe), read retorna o status de saída 1, então quando read retorna 1 sabemos que é feito leitura e processamento, e podemos produzir o total uso que calculamos.

Quanto à produção de dados legíveis, poderíamos usar numfmt ou alguns outros utilitários . Algo como numfmt --to=iec-i --suffix=B $user_usage seria suficiente.

No geral, isso pode ser usado como uma linha, se cortarmos os nomes das variáveis para algo curto, mas não há nenhuma vantagem em ter uma linha única. Basta fazer as coisas da maneira mais correta possível e não se preocupe com o tamanho do código.

Juntando tudo, a solução completa deve ser:

grep 'dook' /etc/trueuserowners | while IFS=":" read -r username trash || { printf "%s\ttotal\n" $( numfmt --to=iec-i --suffix=B "$total"); break;} 
do 
    # get usage in bytes
    user_usage=$( du -sb /home/"$username" | awk '{print }' )
    # get human-readable
    usage_human_readable=$( numfmt --to=iec-i --suffix=B "$user_usage" )
    # output the usage for current user
    printf "%s\t/home/%s\n" "$usage_human_readable"  "$username"
    total=$(($total+$user_usage))
done 
    
por Sergiy Kolodyazhnyy 01.12.2017 / 23:34