Question Comment définir une variable à la sortie d'une commande dans Bash?


J'ai un script assez simple qui ressemble à ceci:

#!/bin/bash

VAR1="$1"    
MOREF='sudo run command against $VAR1 | grep name | cut -c7-'

echo $MOREF

Lorsque j'exécute ce script à partir de la ligne de commande et lui transmets les arguments, je ne reçois aucune sortie. Cependant, lorsque je cours les commandes contenues dans le $MOREF variable, je suis en mesure d'obtenir une sortie.

Je voudrais savoir comment on peut prendre les résultats d'une commande qui doit être exécutée dans un script, l'enregistrer dans une variable, puis afficher cette variable à l'écran?


1085
2018-01-10 20:58


origine


Réponses:


En plus des backticks, vous pouvez utiliser $(), que je trouve plus facile à lire, et permet d'imbrication.

OUTPUT="$(ls -1)"
echo "${OUTPUT}"

Citant (") est important pour préserver les valeurs multilignes.


1661
2018-01-10 21:04



Mettre à jour (2018): le bon chemin est

$(sudo run command)

Vous utilisez le mauvais type d'apostrophe. Vous avez besoin `, ne pas '. Ce personnage est appelé "backticks" (ou "accent grave").

Comme ça:

#!/bin/bash

VAR1="$1"
VAR2="$2"

MOREF=`sudo run command against "$VAR1" | grep name | cut -c7-`

echo "$MOREF"

200
2018-01-10 21:00



Comme ils vous l'ont déjà indiqué, vous devez utiliser des «backticks».

L'alternative proposée $(command) fonctionne aussi, et aussi plus facile à lire, mais notez qu'il est valide seulement avec des coquilles de bash ou de korn (et des coquilles dérivées de ceux-ci), Donc, si vos scripts doivent être vraiment portables sur différents systèmes Unix, vous devriez préférer l'ancienne notation des backticks.


57
2018-01-11 22:14



Je connais trois façons de faire:

1) Les fonctions conviennent à de telles tâches:

func (){
ls -l
}

Invoquez-le en disant func

2) Également une autre solution appropriée pourrait être eval:

var="ls -l"
eval $var

3) Le troisième utilise des variables directement:

var=$(ls -l)
OR
var=`ls -l`

vous pouvez obtenir la sortie de la troisième solution de la bonne manière:

echo "$var"

et aussi de manière méchante:

echo $var

45
2018-02-13 07:31



Certains  astuces que j'utilise pour définir des variables à partir de commandes

2nd Edit 2018-02-12: Ajouter un moyen spécial, voir tout en bas de cette page!

2018-01-25 Edit: ajouter une fonction d'exemple (pour peupler les variables sur l'utilisation du disque)

Première manière simple et ancienne

myPi=`echo '4*a(1)' | bc -l`
echo $myPi 
3.14159265358979323844

Principalement compatible, deuxième voie

Comme l'emboîtement peut devenir lourd, une parenthèse a été implémentée pour cette

myPi=$(bc -l <<<'4*a(1)')

Échantillon imbriqué:

SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted 
1480656334

lire plus d'une variable (avec les bashismes)

df -k /
Filesystem     1K-blocks   Used Available Use% Mounted on
/dev/dm-0         999320 529020    401488  57% /

Si je veux juste Utilisé valeur:

array=($(df -k /))

Tu pourrais voir tableau 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]="/")'

Alors:

echo ${array[9]}
529020

Mais je préfère ça:

{ read foo ; read filesystem size used avail prct mountpoint ; } < <(df -k /)
echo $used
529020

1er read foo juste sauter ligne d'en-tête (variable $foo contiendra quelque chose comme Filesystem 1K-blocks Used Available Use% Mounted on)

Exemple de fonction pour remplir 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 $1
echo $total $used $free

Nota: declare ligne n'est pas nécessaire, juste pour la lisibilité.

Sur 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 une fourchette de moins:

shell=$(grep $USER </etc/passwd | cut -d : -f 7)

Toutes les pipes (|) implique des fourches. Où un autre processus doit être exécuté, accéder au disque, aux appels de bibliothèques et ainsi de suite.

Donc en utilisant sed pour l'échantillon, limitera sous-processus à un seul fourchette:

shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $shell

Et avec les bashismes:

Mais pour de nombreuses actions, principalement sur de petits fichiers,  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 ...

Aller plus loin à propos de division variable...

Jetez un oeil à ma réponse à Comment séparer une chaîne sur un délimiteur dans Bash?

Alternative: réduire fourches en utilisant des tâches de longue durée en arrière-plan

2e édition 2018-02-12: Afin d'éviter plusieurs fourchettes

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 travailler 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 de base pour créer des emplois de façon répétitive, sans avoir à fourchette 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 doit être inutilisé!) ... De là, vous pouvez utiliser ce processus en:

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

Dans une fonction newConnector

Vous pouvez trouver mon newConnector fonctionner sur GitHub.Com ou sur mon propre site (Nota sur github, il y a deux fichiers, sur mon site, la fonction et la démo sont regroupés dans un fichier qui pourrait être source d'utilisation ou juste 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 laissez-vous utiliser la tâche 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 mettre fin à un processus de fond, il vous suffit de 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

ce qui n'est pas nécessaire, car tout se ferme lorsque le processus principal est terminé.


36
2017-12-20 07:06



Juste pour être différent:

MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)

22
2018-01-10 21:07



Si vous voulez le faire avec multiline / multiple command / s alors vous pouvez le faire:

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 unique en un multiligne. Un autre exemple:

output="$( ssh -p $port $user@$domain <<EOF 
#breakdown your long ssh command into multiline here.
EOF
)"

12
2018-06-01 17:38



Lorsque vous définissez une variable, assurez-vous d'avoir Sans 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"

Échec avec erreur: (substance: non trouvé ou similaire)

WTFF= `echo "stuff"`
echo "Example: $WTFF"

10
2017-07-18 11:42



Vous pouvez utiliser des back-ticks (également connus sous le nom de "graves") ou $(). Comme en-

OUTPUT=$(x+2);
OUTPUT=`x+2`;

Les deux ont le même effet. Mais OUTPUT = $ (x + 2) est plus lisible et le dernier.


5
2018-02-09 08:03



C'est un autre moyen, bon à utiliser avec certains éditeurs de texte qui sont incapables de mettre en évidence tous les codes complexes que vous créez.

read -r -d '' str < <(cat somefile.txt)
echo "${#str}"
echo "$str"

3
2018-05-10 21:44