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



title tags (12)

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 in der Variablen $MOREF enthaltenen Befehle $MOREF , kann ich eine Ausgabe erhalten.

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.


Answers

Update (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"

Sie müssen beides 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

Um anders zu sein:

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

Hier sind zwei weitere Möglichkeiten: Bitte beachten Sie, dass Platz in bash sehr wichtig ist. Wenn Sie also möchten, dass Ihr Befehl ausgeführt wird, wie er ist, ohne weitere Leerzeichen einzufügen.

  1. folgend weist L harilil zu und druckt es dann aus

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

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

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) 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

Zusätzlich zu den Backticks können Sie $() , was leichter zu lesen ist und das Verschachteln ermöglicht.

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

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


Einige bash 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 mit Datenträgerbenutzung)

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 Wert verwenden möchte:

array=($(df -k /))

Sie könnten Array- Variable sehen:

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 Kopfzeile überspringen (Variable $foo wird 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

Nota: declare line ist nicht erforderlich, nur zur besseren Lesbarkeit.

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

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

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

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

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

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

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

Und mit Bashismen :

Aber für viele Aktionen, meist auf kleinen Dateien, könnte bash die Aufgabe 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 ...

Gehen Sie weiter über variable Aufteilung ...

Werfen Sie einen Blick auf meine Antwort auf Wie teile ich eine Zeichenfolge auf einem Trennzeichen in Bash?

Alternative: Reduzieren von Abzweigungen durch lang andauernde Aufgaben im Hintergrund

2. Edit 2018-02-12: Um Mehrfachgabeln 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 zeilenweise funktionieren könnten:

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

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

Wir könnten einen lang andauernden Hintergrundprozess verwenden, um Jobs wiederholt zu erstellen, ohne dass für jede Anfrage ein neuer Fork eingeleitet werden muss:

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 benutzt werden!) ... Von dort aus können Sie diesen Prozess 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 meiner eigenen Website finden (Nota auf github, gibt es zwei Dateien, auf meiner Website, Funktion und Demo sind in 1 Datei gebündelt, die zur Verwendung bezogen werden kann oder nur für Demo)

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"

Mit der Funktion myBc 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

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


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

Die alternative vorgeschlagene $(command) funktioniert auch, und es ist auch einfacher zu lesen, aber beachten Sie, dass es nur mit bash oder Korn Shells (und davon abgeleiteten Shells) gültig ist, also wenn Ihre Skripte auf verschiedenen Unix wirklich portierbar sein müssen Systeme sollten Sie die alte Backtick-Notation bevorzugen.


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

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

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


Manche mögen das nützlich finden. Ganzzahlige Werte in 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

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"

The top answer to this question seemed a bit buggy when I tried it -- here's my solution which I've found to be more robust:

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi




bash shell command-line