shell affectation - Comment définir une variable à la sortie d'une commande dans Bash?





calcul globale (12)


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

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 $MOREF les commandes contenues dans la variable $MOREF , je peux 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?




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

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

Citant ( " ) importe pour préserver les valeurs multi-lignes.




Juste pour être différent:

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



Voici deux autres façons: S'il vous plaît gardez à l'esprit que l'espace est très important dans bash, donc si vous voulez que votre commande soit utilisée telle quelle sans introduire plus d'espaces.

  1. suivant assigne harshil à L, puis l'imprime

    L=$"harshil"
    echo "$L"
    
  2. suit assigne la sortie d'une commande tr à L2. tr est exploité sur une autre variable L1.

    L2=$(echo "$L1" | tr [:upper:] [:lower:])
    



Quelques astuces de bash 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 Modifier: 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 des bashismes )

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

Si je veux juste utiliser la valeur:

array=($(df -k /))

vous pouvez voir la variable tableau :

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

La première read foo ne fera que sauter la ligne d'en-tête (la variable $foo contiendra quelque chose comme un 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é.

À propos de sudo cmd | grep ... | cut ... sudo cmd | grep ... | cut ...

shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $shell
/bin/bash

(S'il vous plaît éviter cat inutile! Donc, ce n'est qu'une fourche moins:

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

Tous les tuyaux ( | ) impliquent 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 le sous-processus à une seule fourchette :

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

Et avec les bashismes :

Mais pour beaucoup d'actions, principalement 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 ...

Aller plus loin à propos du fractionnement variable ...

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

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

2e édition 2018-02-12: Afin d'éviter plusieurs fourchettes 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 pourraient fonctionner ligne par ligne :

bc -l <<<$'3*4\n5*6'
12
30

date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288

Nous pourrions utiliser un processus d'arrière-plan à exécution longue pour effectuer des tâches de manière répétitive, sans avoir à lancer une nouvelle fourchette pour chaque requête:

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 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 ma fonction newConnector 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 vous permet d'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 terminer un processus d'arrière-plan, 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é.




Lorsque vous définissez une variable, assurez-vous que vous n'avez 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"

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

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



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

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




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.




Vous devez utiliser soit

$(command-here)

ou

`command-here`

Exemple

#!/bin/bash

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

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

echo $MOREF



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 [email protected]$domain <<EOF 
#breakdown your long ssh command into multiline here.
EOF
)"



Certains peuvent trouver cela utile. Valeurs entières dans la substitution de variables, où l'astuce utilise les doubles crochets $(()) :

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



Et si vous avez un sacré fichier à télécharger depuis un emplacement distant et que vous ne vous souciez pas de la sécurité, essayez de changer le cryptage par défaut scp (Triple-DES) en quelque chose comme "blowfish".

Cela réduira considérablement le temps de copie des fichiers.

scp -c blowfish -r [email protected]:/path/to/foo /home/user/Desktop/




bash shell command-line