sirve - ¿Cómo establecer una variable a la salida de un comando en Bash?
programacion shell linux ejemplos (13)
Tengo un script bastante simple que es algo como lo siguiente:
#!/bin/bash
VAR1="$1"
MOREF=''sudo run command against $VAR1 | grep name | cut -c7-''
echo $MOREF
Cuando ejecuto este script desde la línea de comandos y le paso los argumentos, no obtengo ningún resultado. Sin embargo, cuando ejecuto los comandos contenidos en la variable $MOREF
, puedo obtener salida.
Me gustaría saber cómo se pueden tomar los resultados de un comando que debe ejecutarse dentro de un script, guardarlo en una variable y luego generar esa variable en la pantalla.
Algunos trucos de bash que utilizo para establecer variables a partir de comandos
2ª Edición 2018-02-12: Agregando una forma especial, ¡vea al final de esto!
2018-01-25 Edición: agregar una función de ejemplo (para poblar vars sobre el uso del disco)
Primera forma antigua y compatible simple.
myPi=`echo ''4*a(1)'' | bc -l`
echo $myPi
3.14159265358979323844
Mayormente compatible, segunda vía.
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
Leer más de una variable (con bashismos ).
df -k /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/dm-0 999320 529020 401488 57% /
Si solo quiero valor usado :
array=($(df -k /))
usted podría ver la 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 used avail prct mountpoint ; } < <(df -k /)
echo $used
529020
La primera read foo
solo saltará la línea del encabezado (la variable $foo
contendrá algo así como el Filesystem 1K-blocks Used Available Use% Mounted on
)
Función de ejemplo para poblar 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: no se requiere declare
línea, solo para facilitar la lectura.
Sobre sudo cmd | grep ... | cut ...
sudo cmd | grep ... | cut ...
shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash
(¡Por favor, evite el cat
inútil! Así que esto es solo 1 horquilla menos:
shell=$(grep $USER </etc/passwd | cut -d : -f 7)
Todos los tubos ( |
) implican horquillas. Donde se debe ejecutar otro proceso, acceder al disco, llamar a bibliotecas, etc.
Por lo tanto, utilizar sed
para la muestra, limitará el subproceso a una sola bifurcación :
shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell
Y con los bashismos :
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 allá 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 larga duración en segundo plano
2nd Edit 2018-02-12: para evitar múltiples bifurcaciones 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)
Y porque date
y bc
podrían trabajar línea por línea :
bc -l <<<$''3*4/n5*6''
12
30
date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288
Podríamos utilizar un proceso en segundo plano de larga ejecución para realizar trabajos de forma repetitiva, sin tener que iniciar una nueva bifurcación para cada solicitud:
mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc
(¡Por supuesto, FD 5
y 6
deben estar sin uso!) ... 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 función newConnector
en GitHub.Com o en mi propio sitio (Nota en github, hay dos archivos, en mi sitio, la función y la demo están agrupadas en 1 archivo que puede obtenerse para su uso o simplemente ejecutar la demo)
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 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
que no es necesario, porque todos los fd se cierran cuando finaliza el proceso principal.
Además de backticks ( `command`
), puedes usar $(command)
, que me parece más fácil de leer y me permite anidar.
OUTPUT="$(ls -1)"
echo "${OUTPUT}"
La cotización ( "
) es importante para preservar los valores de multilínea.
Al establecer una variable, asegúrese de que NO haya espacios antes y / o después del signo = . Literalmente pasé una hora tratando de resolver esto, ¡probando todo tipo de soluciones! Esto no es guay.
Correcto:
WTFF=`echo "stuff"`
echo "Example: $WTFF"
Fallará con error: (cosas: no se encuentra o similar)
WTFF= `echo "stuff"`
echo "Example: $WTFF"
Algunos pueden encontrar esto útil. Valores enteros en la sustitución de variables, donde el truco usa $(())
paréntesis dobles:
N=3
M=3
COUNT=$N-1
ARR[0]=3
ARR[1]=2
ARR[2]=4
ARR[3]=1
while (( COUNT < ${#ARR[@]} ))
do
ARR[$COUNT]=$((ARR[COUNT]*M))
(( COUNT=$COUNT+$N ))
done
Aquí hay dos formas más:
Por favor, tenga en cuenta que el espacio es muy importante en bash . Por lo tanto, si desea que su comando se ejecute, úselo tal cual sin introducir más espacios.
A continuación se asigna Harshil a L y luego se imprime.
L=$"harshil" echo "$L"
A continuación se asigna la salida del comando
tr
a L2.tr
está siendo operado en otra variable L1.L2=$(echo "$L1" | tr [:upper:] [:lower:])
Como ya te han indicado, debes usar ''backticks''.
La alternativa propuesta $(command)
funciona, y también es más fácil de leer, pero tenga en cuenta que solo es válida con shells bash o korn (y shells derivados de ellos), así que si sus scripts tienen que ser realmente portátiles en varios Unix En sistemas, deberías preferir la notación de backticks anterior.
Conozco tres formas de hacer:
1) Las funciones son adecuadas para tales tareas:
func (){
ls -l
}
Invocalo diciendo func
2) También otra solución adecuada podría ser eval:
var="ls -l"
eval $var
3) El tercero está usando variables directamente:
var=$(ls -l)
OR
var=`ls -l`
Usted puede obtener la salida de la tercera solución de buena manera:
echo "$var"
y también de manera desagradable:
echo $var
Esta es otra forma, buena para usar con algunos editores de texto que no pueden resaltar correctamente cada código intrincado que creas.
read -r -d '''' str < <(cat somefile.txt)
echo "${#str}"
echo "$str"
Necesitas usar cualquiera
$(command-here)
o
`command-here`
ejemplo
#!/bin/bash
VAR1="$1"
VAR2="$2"
MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)"
echo "$MOREF"
Puede usar garrapatas (también conocidas como tumbas de acento) o $()
. Como-
OUTPUT=$(x+2);
OUTPUT=`x+2`;
Ambos tienen el mismo efecto. Pero OUTPUT = $ (x + 2) es más legible y el más reciente.
Si desea hacerlo con multiline / multiple command / s, entonces puede hacer esto:
output=$( bash <<EOF
#multiline/multiple command/s
EOF
)
O:
output=$(
#multiline/multiple command/s
)
Ejemplo:
#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"
Salida:
first
second
third
Usando heredoc , puede simplificar las cosas bastante fácilmente dividiendo su código de una sola línea larga en uno multilínea. Otro ejemplo:
output="$( ssh -p $port $user@$domain <<EOF
#breakdown your long ssh command into multiline here.
EOF
)"
Solo para ser diferente:
MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
Actualización (2018): el camino correcto es
$(sudo run command)
Estás usando el tipo equivocado de apóstrofe. Necesitas `
, no ''
. Este personaje se llama "comillas invertidas" (o "acento grave").
Me gusta esto:
#!/bin/bash
VAR1="$1"
VAR2="$2"
MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-`
echo "$MOREF"