Algunos trucos de Bash que uso para establecer variables a partir de comandos
2nd Edit 2018-02-12: Agregado de una manera diferente, ¡busca en la parte inferior de este para tareas de larga duración !
25-01-2018 Editar: se agregó una función de muestra (para rellenar variables sobre el uso del disco)
Primera forma simple, antigua y compatible
myPi=`echo '4*a(1)' | bc -l`
echo $myPi
3.14159265358979323844
Mayormente compatible, segunda forma
Como la anidación podría volverse pesada, se implementó un paréntesis para esto
myPi=$(bc -l <<<'4*a(1)')
Muestra anidada:
SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted
1480656334
Lectura de más de una variable (con Bashisms )
df -k /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/dm-0 999320 529020 401488 57% /
Si solo quiero un valor usado :
array=($(df -k /))
podrías ver una variable 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]="/")'
Entonces:
echo ${array[9]}
529020
Pero prefiero esto:
{ read foo ; read filesystem size using avail prct mountpoint ; } < <(df -k /)
echo $using
529020
El primero read foo
simplemente saltará la línea del encabezado, pero en un solo comando, completará 7 variables diferentes :
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"
O incluso:
{ 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")
... también funcionará con matrices asociativas :read foo disk[total] disk[used] ...
Función de muestra para rellenar algunas variables:
#!/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: declare
no se requiere línea, solo para facilitar la lectura.
Acerca de sudo cmd | grep ... | cut ...
shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash
(¡Evite inútil cat
! Así que esto es solo un tenedor menos:
shell=$(grep $USER </etc/passwd | cut -d : -f 7)
Todos los tubos ( |
) implican horquillas. Donde se debe ejecutar otro proceso, acceder al disco, llamadas a bibliotecas, etc.
Por lo tanto, usar sed
para la muestra limitará el subproceso a solo una bifurcación :
shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell
Y con Bashisms :
Pero para muchas acciones, principalmente en archivos pequeños, Bash podría hacer el trabajo por sí mismo:
while IFS=: read -a line ; do
[ "$line" = "$USER" ] && shell=${line[6]}
done </etc/passwd
echo $shell
/bin/bash
o
while IFS=: read loginname encpass uid gid fullname home shell;do
[ "$loginname" = "$USER" ] && break
done </etc/passwd
echo $shell $loginname ...
Yendo más lejos sobre la división de variables ...
Eche un vistazo a mi respuesta a ¿Cómo divido una cadena en un delimitador en Bash?
Alternativa: reducir las horquillas utilizando tareas de fondo de larga duración
2nd Edit 2018-02-12:
Para evitar múltiples horquillas como
myPi=$(bc -l <<<'4*a(1)'
myRay=12
myCirc=$(bc -l <<<" 2 * $myPi * $myRay ")
o
myStarted=$(date -d "$(ps ho lstart 1)" +%s)
mySessStart=$(date -d "$(ps ho lstart $$)" +%s)
Esto funciona bien, pero ejecutar muchos tenedores es pesado y lento.
¡Y los comandos como date
y bc
podrían hacer muchas operaciones, línea por línea !
Ver:
bc -l <<<$'3*4\n5*6'
12
30
date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288
Por lo tanto, podríamos utilizar un proceso en segundo plano de larga ejecución para realizar muchos trabajos, sin tener que iniciar una nueva bifurcación para cada solicitud.
Solo necesitamos algunos descriptores de archivo y quince para hacerlo correctamente:
mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc
(¡Por supuesto, FD 5
y 6
tiene que estar sin usar!) ... A partir de ahí, puede usar este proceso de la siguiente manera:
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
En una función newConnector
Puede encontrar mi newConnector
función en GitHub.Com o en mi propio sitio (Nota sobre GitHub: hay dos archivos en mi sitio. La función y la demostración están agrupadas en un solo archivo que puede obtenerse para su uso o simplemente ejecutarse para la demostración).
Muestra:
. 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"
La función le myBc
permite usar la tarea en segundo plano con una sintaxis simple y para la fecha:
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 de ahí, si desea finalizar uno de los procesos en segundo plano, solo tiene que cerrar su 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
lo cual no es necesario, porque todos los archivos se cierran cuando finaliza el proceso principal.