it-swarm.com.de

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

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

#!/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 ihm die Argumente übergebe, erhalte ich keine Ausgabe. Wenn ich jedoch die in der Variablen $MOREF enthaltenen Befehle ausführe, kann ich eine Ausgabe erhalten.

Wie kann man die Ergebnisse eines Befehls, der in einem Skript ausgeführt werden muss, in einer Variablen speichern und diese Variable dann auf dem Bildschirm ausgeben?

1416
John

Zusätzlich zu den Backticks `command` können Sie $(command) oder "$(command)" verwenden, die meiner Meinung nach leichter zu lesen sind und das Verschachteln ermöglichen.

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

MULTILINE=$(ls \
   -1)
echo "${MULTILINE}"

Das Zitieren (") ist wichtig, um mehrzeilige Werte beizubehalten.

2109
Andy Lester

Der richtige Weg ist

$(Sudo run command)

Wenn Sie ein Apostroph verwenden möchten, benötigen Sie `, 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"
254
Ilya Kogan

Wie Sie bereits wissen, sollten Sie Backticks verwenden.

Die vorgeschlagene Alternative $(command) funktioniert ebenso gut und ist auch einfacher zu lesen. Beachten Sie jedoch, dass sie nur mit Bash oder KornShell (und von diesen abgeleiteten Shells) gültig ist, wenn Ihre Skripte also wirklich auf verschiedenen Unix-Systemen portierbar sein müssen Systeme sollten Sie die alte Backticks-Notation bevorzugen.

67
bitwelder

Einige bash Tricks, mit denen ich Variablen aus Befehlen setze

2nd Edit 2018-02-12: Wenn Sie einen anderen Weg wählen, suchen Sie unten nach lang laufenden Aufgaben !

25.01.2018 Bearbeiten: Beispielfunktion hinzufügen (zum Auffüllen von Variablen zur Datenträgernutzung)

Erster einfacher alter und kompatibler Weg

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

Meist kompatibel, zweiter Weg

Da das Verschachteln schwer werden konnte, wurde dafür eine Klammer implementiert

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

Verschachteltes Beispiel:

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

lesen von mehr als einer Variablen (mit Bashismen )

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

Wenn ich nur den Wert Used haben möchte:

array=($(df -k /))

sie könnten 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 überspringt die Kopfzeile (Variable $foo enthält so etwas wie 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

Hinweis: 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 vermeide nutzlose cat! Das ist also nur 1 Gabel weniger:

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

Alle Pipes (|) implizieren Gabeln. Wo ein anderer Prozess ausgeführt werden muss, Zugriff auf Datenträger, Bibliotheksaufrufe usw.

Wenn Sie also sed als Beispiel verwenden, wird der Unterprozess auf nur eine Gabel beschränkt:

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

Und mit Bashismen :

Aber für viele Aktionen, meistens für kleine Dateien, könnte bash 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 ...

Weitere Informationen zur Variablenaufteilung ...

Schauen Sie sich meine Antwort auf an. Wie teile ich einen String an einem Trennzeichen in Bash auf?

Alternative: Reduzieren von Gabeln durch Verwendung von im Hintergrund ausgeführten Aufgaben

2nd Edit 2018-02-12: Um Mehrfachgabeln wie

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)

Dies funktioniert gut, aber viele Gabeln laufen ist schwer und langsam.

und Befehle wie date und bc können viele Operationen ausführen, Zeile für Zeile !!

Sehen:

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

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

Wir könnten also einen lang laufenden Hintergrundprozess verwenden, um viele Jobs zu erstellen, ohne für jede Anfrage einen neuen Fork auslösen zu müssen.

Wir brauchen nur einige Dateideskriptoren und Fifos , um dies richtig zu machen:

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

(Natürlich müssen FD 5 und 6 nicht verwendet werden!) ... Von dort aus können Sie diesen Prozess folgendermaßen verwenden:

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

Sie können meine newConnector -Funktion auf GitHub.Com oder auf meine eigene Site (Hinweis auf Github, es gibt zwei Dateien auf meiner Site, Funktion und Demo sind in 1 Datei gebündelt, die für die Verwendung oder nur für die Demo ausgeführt werden könnte)

Stichprobe:

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

Mit der Funktion myBc können Sie die Hintergrundaufgabe mit einfacher Syntax und für Datum verwenden:

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

Wenn Sie von dort aus einen Hintergrundprozess beenden möchten, müssen Sie nur sein fd schließen:

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

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

62
F. Hauri

Ich kenne drei Möglichkeiten:

1) Funktionen sind für solche Aufgaben geeignet:

func (){
ls -l
}

Rufen Sie es auf, indem Sie func sagen.

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

var="ls -l"
eval $var

) Der dritte verwendet Variablen direkt:

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

sie können die Ausgabe der dritten Lösung auf gute Weise erhalten:

echo "$var"

und auch in böser Weise:

echo $var
52
MLSC

Nur um anders zu sein:

MOREF=$(Sudo run command against $VAR1 | grep name | cut -c7-)
24
DigitalRoss

Stellen Sie beim Setzen einer Variablen sicher, dass KEINE Leerzeichen vor und/oder nach dem = Zeichen steht. Verbrachte buchstäblich eine Stunde damit, das herauszufinden und alle möglichen Lösungen auszuprobieren! Das ist nicht cool.

Richtig:

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

Wird fehlschlagen mit Fehler: (Zeug: nicht gefunden oder ähnlich)

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

Wenn Sie dies mit mehrzeiligen/mehreren Befehlen tun möchten, 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

Mit heredoc können Sie die Dinge ziemlich einfach vereinfachen, indem Sie Ihren langen Code für eine einzelne Zeile in einen Code für mehrere Zeilen aufteilen. Ein anderes Beispiel:

output="$( ssh -p $port [email protected]$domain <<EOF 
#breakdown your long ssh command into multiline here.
EOF
)"
13
Jahid

Sie müssen entweder verwenden

$(command-here)

oder

`command-here`

beispiel

#!/bin/bash

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

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

echo "$MOREF"
8
Diego Velez

Dies ist eine weitere Möglichkeit, die sich für einige Texteditoren eignet, die nicht alle von Ihnen erstellten komplexen Codes korrekt hervorheben können.

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

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

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

Beide haben den gleichen Effekt. Aber OUTPUT = $ (x + 2) ist lesbarer und der neueste.

6
Pratik Patil

Einige mögen dies nützlich finden. Ganzzahlige Werte bei der Variablensubstitution, wobei der Trick $(()) doppelte Klammern verwendet:

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
4
Gus

Hier sind zwei weitere Möglichkeiten:

Bitte bedenken Sie, dass das Leerzeichen in bash sehr wichtig ist. Wenn Sie also möchten, dass Ihr Befehl ausgeführt wird, verwenden Sie ihn unverändert, ohne weitere Leerzeichen einzufügen.

  1. im Folgenden wird harshil L zugewiesen und dann gedruckt

    L=$"harshil"
    echo "$L"
    
  2. im Folgenden wird die Ausgabe des Befehls tr L2 zugewiesen. tr wird an einer anderen Variablen L1 betrieben.

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

Wenn der Befehl, den Sie ausführen möchten, fehlschlägt, wird die Ausgabe in den Fehlerstrom geschrieben und dann an die Konsole ausgegeben. Um dies zu vermeiden, müssen Sie den Fehlerstrom umleiten

result=$(ls -l something_that_does_not_exist 2>&1)
1
cafebabe1991