Alguns truques do Bash que eu uso para definir variáveis de comandos
2nd Edit 2018-02-12: Adicionada uma maneira diferente, pesquise na parte inferior desta por tarefas de longa duração !
2018-01-25 Edit: adicionada uma função de amostra (para preencher variáveis sobre o uso do disco)
Primeira maneira simples, antiga e compatível
myPi=`echo '4*a(1)' | bc -l`
echo $myPi
3.14159265358979323844
Principalmente compatível, segunda via
Como o aninhamento pode se tornar pesado, parênteses foram implementados para esse
myPi=$(bc -l <<<'4*a(1)')
Amostra aninhada:
SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted
1480656334
Lendo mais de uma variável (com Bashisms )
df -k /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/dm-0 999320 529020 401488 57% /
Se eu apenas quero um valor usado :
array=($(df -k /))
você pode ver uma variável de matriz :
declare -p array
declare -a array='([0]="Filesystem" [1]="1K-blocks" [2]="Used" [3]="Available" [
4]="Use%" [5]="Mounted" [6]="on" [7]="/dev/dm-0" [8]="999320" [9]="529020" [10]=
"401488" [11]="57%" [12]="/")'
Então:
echo ${array[9]}
529020
Mas eu prefiro isso:
{ read foo ; read filesystem size using avail prct mountpoint ; } < <(df -k /)
echo $using
529020
O primeiro read foo
só vai pular linha de cabeçalho, mas em apenas um comando, você vai preencher 7 diferentes variáveis:
declare -p avail filesystem foo mountpoint prct size using
declare -- avail="401488"
declare -- filesystem="/dev/dm-0"
declare -- foo="Filesystem 1K-blocks Used Available Use% Mounted on"
declare -- mountpoint="/"
declare -- prct="57%"
declare -- size="999320"
declare -- using="529020"
Ou até:
{ read foo ; read filesystem dsk[{6,2,9}] prct mountpoint ; } < <(df -k /)
declare -p mountpoint dsk
declare -- mountpoint="/"
declare -a dsk=([2]="529020" [6]="999320" [9]="401488")
... também funcionará com matrizes associativas :read foo disk[total] disk[used] ...
Função de amostra para preencher algumas variáveis:
#!/bin/bash
declare free=0 total=0 used=0
getDiskStat() {
local foo
{
read foo
read foo total used free foo
} < <(
df -k ${1:-/}
)
}
getDiskStat $1
echo $total $used $free
Nota: a declare
linha não é necessária, apenas para facilitar a leitura.
Sobre sudo cmd | grep ... | cut ...
shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash
(Evite inútil cat
! Portanto, este é apenas um garfo a menos:
shell=$(grep $USER </etc/passwd | cut -d : -f 7)
Todos os tubos ( |
) implica garfos. Onde outro processo precisa ser executado, acessando disco, chamadas de bibliotecas e assim por diante.
Portanto, o uso sed
de amostra limitará o subprocesso a apenas um garfo :
shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell
E com Bashisms :
Mas, para muitas ações, principalmente em arquivos pequenos, o Bash poderia fazer o trabalho sozinho:
while IFS=: read -a line ; do
[ "$line" = "$USER" ] && shell=${line[6]}
done </etc/passwd
echo $shell
/bin/bash
ou
while IFS=: read loginname encpass uid gid fullname home shell;do
[ "$loginname" = "$USER" ] && break
done </etc/passwd
echo $shell $loginname ...
Indo mais longe sobre a divisão de variáveis ...
Dê uma olhada na minha resposta para Como eu divido uma string em um delimitador no Bash?
Alternativa: reduzindo garfos usando tarefas em execução de longa duração em segundo plano
2a Edição 2018-02-12:
Para evitar vários garfos como
myPi=$(bc -l <<<'4*a(1)'
myRay=12
myCirc=$(bc -l <<<" 2 * $myPi * $myRay ")
ou
myStarted=$(date -d "$(ps ho lstart 1)" +%s)
mySessStart=$(date -d "$(ps ho lstart $$)" +%s)
Isso funciona bem, mas executar muitos garfos é pesado e lento.
E comandos como date
e bc
podem fazer muitas operações, linha por linha !
Vejo:
bc -l <<<$'3*4\n5*6'
12
30
date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288
Portanto, poderíamos usar um processo em segundo plano de longa execução para realizar muitos trabalhos, sem precisar iniciar uma nova bifurcação para cada solicitação.
Nós apenas precisamos de alguns descritores de arquivo e fifos para fazer isso corretamente:
mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc
(Claro, o FD 5
e 6
deve ser não utilizado!) ... A partir daí, você pode usar esse processo:
echo "3*4" >&5
read -u 6 foo
echo $foo
12
echo >&5 "pi=4*a(1)"
echo >&5 "2*pi*12"
read -u 6 foo
echo $foo
75.39822368615503772256
Em uma função newConnector
Você pode encontrar minha newConnector
função no GitHub.Com ou no meu próprio site (Nota no GitHub: existem dois arquivos no meu site. A função e a demonstração são agrupadas em um arquivo que pode ser usado para uso ou apenas executado para demonstração.)
Amostra:
. shell_connector.sh
tty
/dev/pts/20
ps --tty pts/20 fw
PID TTY STAT TIME COMMAND
29019 pts/20 Ss 0:00 bash
30745 pts/20 R+ 0:00 \_ ps --tty pts/20 fw
newConnector /usr/bin/bc "-l" '3*4' 12
ps --tty pts/20 fw
PID TTY STAT TIME COMMAND
29019 pts/20 Ss 0:00 bash
30944 pts/20 S 0:00 \_ /usr/bin/bc -l
30952 pts/20 R+ 0:00 \_ ps --tty pts/20 fw
declare -p PI
bash: declare: PI: not found
myBc '4*a(1)' PI
declare -p PI
declare -- PI="3.14159265358979323844"
A função myBc
permite que você use a tarefa em segundo plano com sintaxe simples e para a data:
newConnector /bin/date '-f - +%s' @0 0
myDate '2000-01-01'
946681200
myDate "$(ps ho lstart 1)" boottime
myDate now now ; read utm idl </proc/uptime
myBc "$now-$boottime" uptime
printf "%s\n" ${utm%%.*} $uptime
42134906
42134906
ps --tty pts/20 fw
PID TTY STAT TIME COMMAND
29019 pts/20 Ss 0:00 bash
30944 pts/20 S 0:00 \_ /usr/bin/bc -l
32615 pts/20 S 0:00 \_ /bin/date -f - +%s
3162 pts/20 R+ 0:00 \_ ps --tty pts/20 fw
A partir daí, se você quiser finalizar um dos processos em segundo plano, basta fechar o seu fd :
eval "exec $DATEOUT>&-"
eval "exec $DATEIN>&-"
ps --tty pts/20 fw
PID TTY STAT TIME COMMAND
4936 pts/20 Ss 0:00 bash
5256 pts/20 S 0:00 \_ /usr/bin/bc -l
6358 pts/20 R+ 0:00 \_ ps --tty pts/20 fw
o que não é necessário, porque todos fecham quando o processo principal termina.