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?

1139
demandé sur codeforester 2011-01-10 23:58:02

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.

1728
répondu Andy Lester 2018-09-25 14:33:45

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"
207
répondu Ilya Kogan 2018-04-29 17:08:06

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.

57
répondu bitwelder 2011-01-11 22:14:19

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
47
répondu MLSC 2014-05-17 16:22:55

Some 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, 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.

40
répondu F. Hauri 2018-02-22 11:15:49

Juste pour être différent:

MOREF=$(sudo run command against $VAR1 | grep name | cut -c7-)
22
répondu DigitalRoss 2011-01-10 21:07:40

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
)"
12
répondu Jahid 2015-12-29 11:16:00

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"
12
répondu Emil 2017-07-18 11:42:01

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.

5
répondu Pratik Patil 2016-02-09 08:03:47

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"
4
répondu Aquarius Power 2015-05-10 21:44:04

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
3
répondu Gus 2015-11-22 11:59:46

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"
3
répondu Diego Velez 2018-07-21 07:01:54

voici deux autres façons:

s'il vous Plaît gardez à l'esprit que l'espace est très important dans la . Donc, si vous voulez votre commande à exécuter, utiliser sans introduire de plus d'espaces.

  1. suivant assigne harshil à L et ensuite l'imprime

    L=$"harshil"
    echo "$L"
    
  2. 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:])
    
0
répondu Harshil 2018-08-07 06:58:54