J'ai un script assez simple qui ressemble à ce qui suit:
#!/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 passe les arguments, je ne reçois aucun résultat. Cependant, lorsque je lance les commandes contenues dans la variable $MOREF
, je peux obtenir une sortie.
Comment peut-on 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 backticks `command`
, vous pouvez utiliser $(command)
ou "$(command)"
que je trouve plus facile à lire et à permettre l’imbrication.
OUTPUT="$(ls -1)"
echo "${OUTPUT}"
MULTILINE=$(ls \
-1)
echo "${MULTILINE}"
La citation ("
) est importante pour préserver les valeurs multilignes.
La bonne façon est
$(Sudo run command)
Si vous voulez utiliser une apostrophe, vous avez besoin de `
et non de '
. 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"
Comme ils vous l'ont déjà indiqué, vous devez utiliser des "backticks".
L'alternative proposée $(command)
fonctionne également et est également plus facile à lire, mais notez qu'elle n'est valide qu'avec Bash ou KornShell (et les shells dérivés de ceux-ci), donc si vos scripts doivent être réellement portables sur divers Unix systèmes, vous devriez préférer la vieille notation backticks.
2nd Edit 2018-02-12: Ajout d’une autre manière, recherchez en bas de ceci tâches longues !
2018-01-25 Edit: ajouter un exemple de fonction (pour renseigner les variables relatives à l'utilisation du disque)
myPi=`echo '4*a(1)' | bc -l`
echo $myPi
3.14159265358979323844
La nidification pouvant devenir lourde, une parenthèse a été mise en place pour cela.
myPi=$(bc -l <<<'4*a(1)')
Échantillon imbriqué:
SysStarted=$(date -d "$(ps ho lstart 1)" +%s)
echo $SysStarted
1480656334
df -k /
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/dm-0 999320 529020 401488 57% /
Si je veux juste Used valeur:
array=($(df -k /))
vous pouviez 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]="/")'
Ensuite:
echo ${array[9]}
529020
Mais je préfère ceci:
{ read foo ; read filesystem size used avail prct mountpoint ; } < <(df -k /)
echo $used
529020
La première read foo
ne fera que ignorer la ligne d'en-tête (la variable $foo
contiendra quelque chose comme Filesystem 1K-blocks Used Available Use% Mounted on
)
#!/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
la ligne n'est pas requise, juste pour la lisibilité.
Sudo cmd | grep ... | cut ...
Shell=$(cat /etc/passwd | grep $USER | cut -d : -f 7)
echo $Shell
/bin/bash
(S'il vous plaît évitez inutile cat
! Donc, c'est juste 1 fourchette de moins:
Shell=$(grep $USER </etc/passwd | cut -d : -f 7)
Tous les tuyaux (|
) impliquent des forks. Si un autre processus doit être exécuté, accéder au disque, aux appels aux bibliothèques, etc.
Donc, utiliser sed
comme exemple limitera le sous-processus à un seul fork :
Shell=$(sed </etc/passwd "s/^$USER:.*://p;d")
echo $Shell
Mais pour de nombreuses 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 ...
Regardez ma réponse à Comment diviser une chaîne sur un délimiteur dans Bash?
2nd Edit 2018-02-12: Afin d'éviter que plusieurs fourchettes ne soient 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)
Cela fonctionne bien, mais l'utilisation de plusieurs fourches est lourde et lente.
et des commandes telles que date
et bc
peuvent effectuer de nombreuses opérations, ligne par ligne !!
Voir:
bc -l <<<$'3*4\n5*6'
12
30
date -f - +%s < <(ps ho lstart 1 $$)
1516030449
1517853288
Nous pourrions donc utiliser le processus d'arrière-plan long = pour créer de nombreux travaux, sans avoir à lancer de nouveau fork pour chaque demande.
Nous avons juste besoin de descripteurs de fichier et fifos pour le faire correctement:
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!) ... À partir 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
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, ma fonction et ma démo sont regroupés dans un fichier pouvant être créé ou utilisé pour une démonstration)
É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 en 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
A partir de là, si vous souhaitez mettre fin à 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 tous les fd se ferment à la fin du processus principal.
Je connais trois façons de faire:
1) Les fonctions conviennent à de telles tâches:
func (){
ls -l
}
Invoquez-le en disant func
2) Une autre solution appropriée pourrait également être eval:
var="ls -l"
eval $var
) Le troisième utilise directement les variables:
var=$(ls -l)
OR
var=`ls -l`
vous pouvez obtenir le résultat de la troisième solution dans le bon sens:
echo "$var"
et aussi de manière méchante:
echo $var
Juste pour être différent:
MOREF=$(Sudo run command against $VAR1 | grep name | cut -c7-)
Lorsque vous définissez une variable, assurez-vous que vous avez NO Spaces avant et/ou après le signe =. J'ai littéralement passé une heure à essayer de comprendre cela, à essayer toutes sortes de solutions! C'est pas cool.
Correct:
WTFF=`echo "stuff"`
echo "Example: $WTFF"
échouera avec l'erreur suivante: (objet introuvable ou similaire)
WTFF= `echo "stuff"`
echo "Example: $WTFF"
Si vous voulez le faire avec plusieurs lignes/plusieurs commandes/s, 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 très facilement en décomposant votre code longue ligne unique en plusieurs lignes. Un autre exemple:
output="$( ssh -p $port $user@$domain <<EOF
#breakdown your long ssh command into multiline here.
EOF
)"
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"
C’est un autre moyen, qu’il est bon d’utiliser avec certains éditeurs de texte qui ne peuvent pas mettre en évidence correctement chaque code complexe que vous créez.
read -r -d '' str < <(cat somefile.txt)
echo "${#str}"
echo "$str"
Vous pouvez utiliser des ticks arrière (aussi appelés accent grave) 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 plus récent.
Certains peuvent trouver cela utile. Valeurs entières en substitution de variable, où l’astuce consiste à utiliser $(())
double 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
Voici deux autres moyens:
S'il vous plaît gardez à l'esprit que l'espace est très important dans bash . Donc, si vous voulez que votre commande s'exécute, utilisez-la telle quelle sans introduire plus d'espaces.
suivant assigne harshil à L puis l’imprime
L=$"harshil"
echo "$L"
ce qui suit affecte la sortie de la commande tr
à L2. tr
est utilisé avec une autre variable L1.
L2=$(echo "$L1" | tr [:upper:] [:lower:])
Si la commande que vous essayez d'exécuter échoue, la sortie est écrite dans le flux d'erreur et ensuite imprimée sur la console. Pour l'éviter, vous devez rediriger le flux d'erreur
result=$(ls -l something_that_does_not_exist 2>&1)