Frage Wie setze ich eine Variable auf die Ausgabe eines Befehls in Bash?


Ich habe ein ziemlich einfaches Skript, das ungefähr so ​​ist:

#!/bin/bash

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

echo $MOREF

Wenn ich dieses Skript über die Befehlszeile ausführe und die Argumente übergebe, bekomme ich keine Ausgabe. Wenn ich jedoch die Befehle innerhalb der $MOREF Variable kann ich ausgeben.

Ich würde gerne wissen, wie man die Ergebnisse eines Befehls, der innerhalb eines Skripts ausgeführt werden muss, in einer Variablen speichern und diese Variable dann auf dem Bildschirm ausgeben kann.


1085
2018-01-10 20:58


Ursprung


Antworten:


Zusätzlich zu den Backticks können Sie verwenden $(), die ich leichter zu lesen finde, und das Nesting ermöglicht.

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

Zitieren (") ist wichtig, um mehrzeilige Werte zu erhalten.


1661
2018-01-10 21:04



Aktualisieren (2018): Der richtige Weg ist

$(sudo run command)

Sie verwenden die falsche Art von Apostroph. Du brauchst `nicht '. Dieser Charakter wird "Backticks" (oder "Grabakzent") genannt.

So was:

#!/bin/bash

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

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

echo "$MOREF"

200
2018-01-10 21:00



Wie Sie bereits angedeutet haben, sollten Sie "Backticks" verwenden.

Die vorgeschlagene Alternative $(command) funktioniert auch, und es ist auch einfacher zu lesen, aber beachte, dass es nur mit Bash- oder Kornschalen (und davon abgeleiteten Schalen) gültig ist, Wenn also Ihre Skripte auf verschiedenen Unix-Systemen wirklich portabel sein müssen, sollten Sie die alte Backtick-Notation bevorzugen.


57
2018-01-11 22:14



Ich kenne drei Möglichkeiten:

1) Funktionen sind für solche Aufgaben geeignet:

func (){
ls -l
}

Rufen Sie es an, indem Sie sagen func

2) Auch eine andere geeignete Lösung könnte eval sein:

var="ls -l"
eval $var

3) Der dritte verwendet Variablen direkt:

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

Sie können die Ausgabe der dritten Lösung in guter Weise erhalten:

echo "$var"

und auch auf böse Weise:

echo $var

45
2018-02-13 07:31



Etwas  Tricks, mit denen ich Variablen aus Befehlen setze

2. Edit 2018-02-12: Einen speziellen Weg hinzufügen, siehe ganz unten!

2018-01-25 Bearbeiten: Beispielfunktion hinzufügen (zum Füllen von Variablen über die Plattennutzung)

Erster einfacher alter und kompatibler Weg

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

Meist kompatibel, zweiter Weg

Da die Verschachtelung schwer werden könnte, wurde dafür eine Klammer eingefügt

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

Verschachteltes Beispiel:

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

mehr als eine Variable lesen (mit Bashismen)

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

Wenn ich nur will Benutzt Wert:

array=($(df -k /))

Du könntest sehen 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]="/")'

Dann:

echo ${array[9]}
529020

Aber ich bevorzuge das:

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

1 read foo wird nur überspringen Kopfzeile (Variable $foo wird etwas wie enthalten Filesystem 1K-blocks Used Available Use% Mounted on)

Beispielfunktion zum Auffüllen einiger Variablen:

#!/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

Kein: declare Zeile ist nicht erforderlich, nur für die Lesbarkeit.

Über sudo cmd | grep ... | cut ...

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

(Bitte vermeiden Sie nutzlos cat! Das ist also nur 1 Gabel weniger:

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

Alle Rohre (|) impliziert Gabeln. Wo ein anderer Prozess ausgeführt werden muss, Zugriff auf Datenträger, Bibliothekenaufrufe usw.

Also benutzen sed Für Beispiel wird der Unterprozess auf nur einen beschränkt Gabel:

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

Und mit Bashismen:

Aber für viele Aktionen, meist auf kleinen Dateien,  könnte den Job selbst erledigen:

while IFS=: read -a line ; do
    [ "$line" = "$USER" ] && shell=${line[6]}
  done </etc/passwd
echo $shell
/bin/bash

oder

while IFS=: read loginname encpass uid gid fullname home shell;do
    [ "$loginname" = "$USER" ] && break
  done </etc/passwd
echo $shell $loginname ...

Weiter gehen variable Aufteilung...

Schau dir meine Antwort an Wie spalte ich eine Zeichenfolge auf einem Trennzeichen in Bash?

Alternative: Reduzieren Gabeln durch Verwendung lang andauernder Aufgaben im Hintergrund

2. Bearbeitung 2018-02-12: Um mehrere Gabeln wie zu verhindern

myPi=$(bc -l <<<'4*a(1)'
myRay=12
myCirc=$(bc -l <<<" 2 * $myPi * $myRay ")

oder

myStarted=$(date -d "$(ps ho lstart 1)" +%s)
mySessStart=$(date -d "$(ps ho lstart $$)" +%s)

Und weil date und bc könnte klappen Zeile für Zeile:

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

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

Wir könnten es gebrauchen lange läuft Hintergrundprozess, um Jobs wiederholt zu erstellen, ohne neue initiieren zu müssen Gabel für jede Anfrage:

mkfifo /tmp/myFifoForBc
exec 5> >(bc -l >/tmp/myFifoForBc)
exec 6</tmp/myFifoForBc
rm /tmp/myFifoForBc

(natürlich, FD 5 und 6 muss ungenutzt sein!) ... Von dort aus könntest du diesen Prozess benutzen:

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

In eine Funktion newConnector

Du kannst meine finden newConnector Funktion an GitHub.Com oder weiter meine eigene Seite (Nota auf Github, es gibt zwei Dateien, auf meiner Website sind Funktion und Demo in 1 Datei gebündelt, die zur Verwendung bereitgestellt werden kann oder einfach nur zur Demo laufen kann)

Probe:

. 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"

Die Funktion myBc Verwenden Sie die Hintergrundaufgabe mit einfacher Syntax und für Datum:

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

Von dort aus, wenn Sie einen Hintergrundprozess beenden möchten, müssen Sie nur seine schließen 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

das wird nicht benötigt, weil alle fd schließen, wenn der Hauptprozess beendet ist.


36
2017-12-20 07:06



Um anders zu sein:

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

22
2018-01-10 21:07



Wenn Sie dies mit mehrzeiligen / mehreren Befehlen tun wollen, können Sie dies tun:

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

Oder:

output=$(
#multiline/multiple command/s
)

Beispiel:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

Ausgabe:

first
second
third

Verwenden Heredoc Sie können die Dinge ziemlich einfach vereinfachen, indem Sie Ihren langen einzeiligen Code in multiline zerlegen. Ein anderes Beispiel:

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

12
2018-06-01 17:38



Stellen Sie sicher, dass Sie eine Variable gesetzt haben Kein Platz vor und / oder nach dem = Schild. Buchstäblich verbrachte eine Stunde versucht, dies herauszufinden, versuchen alle Arten von Lösungen! Das ist nicht cool.

Richtig:

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

Wird versagen mit Fehler: (Sachen: nicht gefunden oder ähnlich)

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

10
2017-07-18 11:42



Sie können Back-Ticks (auch Akzentgräber genannt) oder $(). Wie as-

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

Beides hat den gleichen Effekt. Aber OUTPUT = $ (x + 2) ist lesbarer und die letzte.


5
2018-02-09 08:03



Dies ist eine andere Möglichkeit, die Sie mit einigen Texteditoren verwenden können, die nicht jeden komplizierten Code, den Sie erstellen, richtig hervorheben können.

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

3
2018-05-10 21:44