Comment définir une variable à la sortie d'une commande de Bash?
j'ai un script assez simple qui ressemble à ce qui suit:
#!/bin/bash
VAR1=""
MOREF='sudo run command against $VAR1 | grep name | cut -c7-'
echo $MOREF
quand j'exécute ce script depuis la ligne de commande et lui passe les arguments, je n'obtiens aucune sortie. Cependant, lorsque j'exécute les commandes contenues dans la variable $MOREF
, je suis capable d'obtenir la sortie.
je voudrais savoir comment on peut prendre les résultats d'une commande qui doit être exécutée à l'intérieur d'un script, l'enregistrer dans une variable, puis sortie cette variable sur l'écran?
13 réponses
en plus des bâtons arrière ( `command`
), vous pouvez utiliser $(command)
, que je trouve plus facile à lire, et permet la nidification.
OUTPUT="$(ls -1)"
echo "${OUTPUT}"
Le fait de citer ( "
) est important pour préserver les valeurs multilignes.
mise à Jour (2018): le droit chemin est
$(sudo run command)
Vous utilisez le mauvais type d'apostrophe. Vous avez besoin de `
, pas de '
. Ce personnage est appelé "backticks" (ou "accent grave").
comme ceci:
#!/bin/bash
VAR1=""
VAR2=""
MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-`
echo "$MOREF"
comme ils vous l'ont déjà indiqué, vous devez utiliser des 'bâtons'.
l'alternative proposée $(command)
fonctionne également, et il est également plus facile de lire,
mais notez qu'il n'est valable qu'avec des coquilles de bash ou korn (et des coquilles dérivées de ces),
donc, si vos scripts doivent être vraiment portables sur différents systèmes Unix, vous devriez préférer la notation des vieux bâtons.
je sais que trois façons de le faire:
1) les fonctions sont adaptées à de telles tâches:
func (){
ls -l
}
Invoquez-le en disant func
2) Une autre solution appropriée pourrait également être éval:
var="ls -l"
eval $var
3) le troisième utilise directement les variables:
var=$(ls -l)
OR
var=`ls -l`
vous pouvez obtenir la sortie de troisième bonne solution:
echo "$var"
et aussi de manière méchante:
echo $var
Some bash trucs que j'utilise pour définir des variables à partir de commandes
2nd Edit 2018-02-12: ajouter une voie spéciale, voir au fond de cela!
2018-01-25 modifier: ajouter la fonction d'échantillon (pour peupler les variables sur l'utilisation du disque)
Premier simple de vieux et de façon compatible avec la
myPi=`echo '4*a(1)' | bc -l`
echo $myPi
3.14159265358979323844
plutôt compatible, second way
comme la nidification pourrait devenir lourde, la parenthèse a été mise en œuvre pour ce
myPi=$(bc -l <<<'4*a(1)')
échantillon emboîté:
SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted
1480656334
lecture de plus d'une variable (avec bashismes )
df -k /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/dm-0 999320 529020 401488 57% /
Si je veux Utilisé valeur:
array=($(df -k /))
vous pourriez voir array variable:
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]="/")'
puis:
echo ${array[9]}
529020
mais je préfère ceci:
{ read foo ; read filesystem size used avail prct mountpoint ; } < <(df -k /)
echo $used
529020
1er read foo
vient de skip ligne d'en-tête (variable $foo
contiendra quelque chose comme Filesystem 1K-blocks Used Available Use% Mounted on
)
fonction D'échantillonnage pour la saisie de certaines 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
echo $total $used $free
Nota: la ligne declare
n'est pas nécessaire, seulement pour des raisons de lisibilité.
À propos de sudo cmd | grep ... | cut ...
shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash
(s'il vous plaît éviter inutile cat
! Donc c'est juste 1 fourchette de moins:
shell=$(grep $USER </etc/passwd | cut -d : -f 7)
toutes les pipes ( |
) impliquent des fourches. Là où un autre processus doit être exécuté, l'accès au disque, les appels de bibliothèques et ainsi de suite.
ainsi, en utilisant sed
pour l'échantillon, va limiter le sous-processus à un seul fourche :
shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell
et avec bashismes :
mais pour beaucoup d'actions, surtout sur de petits fichiers, bash pourrait faire le travail lui-même:
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 ...
d'Aller plus loin à propos de "1519580920 variable" splitting ...
regardez ma réponse à Comment séparer une chaîne sur un délimiteur en Bash?
Alternative: réduction des fourches en utilisant des tâches de fond de longue durée
2ème Edition 2018-02-12: Afin de prévenir les fourches multiples comme
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)
et parce que date
et bc
pourrait fonctionner ligne par ligne :
bc -l <<<$'3*4\n5*6'
12
30
date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288
Nous pourrions utiliser longue course processus d'arrière-plan pour rendre les emplois de façon répétitive, sans avoir à lancer de nouvelles fourche pour chaque demande:
mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc
(bien sûr, FD 5
et 6
doivent être inutilisés!)... De là, vous pouvez utiliser ce processus par:
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 fonction newConnector
Vous pouvez trouvé mon newConnector
la fonction GitHub.Com ou sur mon propre site (Nota sur github, il y a deux fichiers, sur mon site, de la fonction et de démonstration sont regroupés dans 1 fichier qui pourrait être d'origine pour une utilisation ou tout simplement courir pour la démo)
échantillon:
. 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 fonction myBc
vous permettent d'utiliser les tâches d'arrière-plan avec une syntaxe simple, et pour la date:
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
de là, si vous voulez en finir un de processus de fond, vous avez juste à fermer son 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
qui n'est pas nécessaire, car tous les fd se ferment à la fin du processus principal.
Juste pour être différent:
MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
Si vous voulez le faire avec multiligne/commande plusieurs/s, alors vous pouvez faire ceci:
output=$( bash <<EOF
#multiline/multiple command/s
EOF
)
ou:
output=$(
#multiline/multiple command/s
)
exemple:
#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"
sortie:
first
second
third
en utilisant heredoc vous pouvez simplifier les choses assez facilement en décomposant votre long code de ligne simple en multiligne un. Un autre exemple:
output="$( ssh -p $port $user@$domain <<EOF
#breakdown your long ssh command into multiline here.
EOF
)"
lorsque vous définissez une variable, assurez-vous d'avoir pas D'espaces avant et/ou après le signe = . Littéralement passé une heure à essayer de comprendre cela, en essayant toutes sortes de solutions! Ce n'est Pas cool.
Correct:
WTFF=`echo "stuff"`
echo "Example: $WTFF"
Échouent avec l'erreur: (les choses: on ne trouve pas ou similaire)
WTFF= `echo "stuff"`
echo "Example: $WTFF"
vous pouvez utiliser back-ticks(aussi connu sous le nom de graves accent) ou $()
.
Comme as -
OUTPUT=$(x+2);
OUTPUT=`x+2`;
ont le même effet. Mais OUTPUT=$(x+2) est plus lisible et le dernier.
c'est une autre façon, bon à utiliser avec certains éditeurs de texte qui sont incapables de mettre en évidence correctement chaque code complexe que vous créez.
read -r -d '' str < <(cat somefile.txt)
echo "${#str}"
echo "$str"
Certains peuvent trouver cela utile.
Valeurs entières dans la substitution de variables, où l'astuce est d'utiliser $(())
doubles parenthèses:
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
vous devez utiliser l'un ou l'autre
$(command-here)
ou
`command-here`
exemple
#!/bin/bash
VAR1=""
VAR2=""
MOREF="$(sudo run command against "$VAR1" | grep name | cut -c7-)"
echo "$MOREF"
voici deux autres façons:
s'il vous Plaît gardez à l'esprit que l'espace est très important dans la bash . Donc, si vous voulez votre commande à exécuter, utiliser sans introduire de plus d'espaces.
-
suivant assigne harshil à L et ensuite l'imprime
L=$"harshil" echo "$L"
-
suivant affecte la sortie de la commande
tr
à la L2.tr
est en cours exploité sur une autre variable L1.L2=$(echo "$L1" | tr [:upper:] [:lower:])