it-swarm-tr.com

Bash'deki bir komutun çıktısına bir değişkeni nasıl ayarlarım?

Aşağıdaki gibi bir şey oldukça basit bir komut dosyası var:

#!/bin/bash

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

echo $MOREF

Bu betiği komut satırından çalıştırdığımda ve argümanları ilettiğimde herhangi bir çıktı alamıyorum. Ancak, $MOREF değişkeni içindeki komutları çalıştırdığımda çıktı alabiliyorum.

Bir komut dosyasında çalıştırılması gereken bir komutun sonuçlarını nasıl alabilir, bir değişkene kaydedebilir ve daha sonra bu değişkeni ekranda gösterebilir?

1317
John

Geri tepmelere (`command`) ek olarak, okumasını daha kolay bulduğum ve yerleştirmeye izin verdiğim $(command) işlevini kullanabilirsiniz.

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

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

Tırnak işareti (") çok satırlı değerleri korumak için önemlidir.

1989
Andy Lester

Doğru yolu

$(Sudo run command)

Kesme işareti kullanacaksanız, ` yerine ' gerekir. Bu karakter "backticks" (veya "Grave accent") olarak adlandırılır.

Bunun gibi:

#!/bin/bash

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

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

echo "$MOREF"
240
Ilya Kogan

Size daha önce de belirttikleri gibi, 'backticks' kullanmalısınız.

Önerilen alternatif $(command) işlevi de çalışır ve okunması da kolaydır, ancak bunun yalnızca Bash veya KornShell (ve bunlardan türetilen kabukları) ile geçerli olduğunu unutmayın, bu nedenle komut dosyalarınızın çeşitli Unix sistemlerinde gerçekten taşınabilir olması gerekiyorsa, eski backticks gösterimini tercih et.

64
bitwelder

Değişkenleri komutlardan ayarlamak için kullandığım bazı bash numaraları

2nd Edit 2018-02-12: Farklı bir yol ekleyerek, bunun altında uzun süre çalışan görevler için arama yapın!

2018-01-25 Düzenleme: örnek işlevi ekleme (disk kullanımıyla ilgili değişkenleri doldurmak için)

İlk basit eski ve uyumlu yol

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

Çoğunlukla uyumlu, ikinci yol

Yuvalama ağırlaşabileceği için parantez uygulandı.

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

İç içe örnek:

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

birden fazla değişken okuma (bashisms ile)

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

Sadece istersem Kullanılmış değer:

array=($(df -k /))

array değişkenini görebilirsiniz:

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]="/")'

Sonra:

echo ${array[9]}
529020

Ama bunu tercih ederim:

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

1. read foo sadece atlayacaktır başlık satırı (değişken $fooFilesystem 1K-blocks Used Available Use% Mounted on gibi bir şey içerecektir)

Bazı değişkenleri doldurmak için örnek fonksiyon:

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

Bilginize: declare satırı gerekli değildir, sadece okunabilirlik için.

Sudo cmd | grep ... | cut ... hakkında

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

(Lütfen faydasız cat'dan kaçının! Yani bu sadece 1 çatal daha az:

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

Tüm borular (|) çatalları ifade eder. Başka bir işlemin yürütülmesi gereken yerlerde, diske erişme, kütüphaneler çağırır vb.

Bu nedenle, örnek için sed kullanmak, alt işlemi yalnızca bir fork ile sınırlandırır:

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

Ve bashisms ile:

Ancak çoğu eylemde, çoğunlukla küçük dosyalarda, bash işi kendisi yapabilir:

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

veya

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

Değişken bölme hakkında daha fazla bilgi ...

Cevabımı inceleyin Bash'deki bir sınırlayıcıdaki dizgiyi nasıl bölerim?

Alternatif: azaltma çatal, arka plan uzun süren görevleri kullanarak

2. Düzenleme 2018-02-12:Gibi çoklu çatalları önlemek için

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

veya

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

Bu iyi çalışıyor, ancak birçok çatal çalıştırmak ağır ve yavaştır.

ve date ve bc gibi komutlar birçok işlem yapabilir, satır satır !!

Görmek:

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

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

Bu nedenle, her bir istek için yeni bir çatal başlatmak zorunda kalmadan birçok iş yapmak için long running background işlemini kullanabiliriz.

Bunu düzgün bir şekilde yapmak için bazı dosya tanımlayıcılarına ve fifos ihtiyacımız var:

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

(elbette, FD 5 ve 6 kullanılmamış olması gerekir!) ... Oradan, şu işlemi kullanarak kullanabilirsiniz:

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

Bir işleve newConnector

newConnector işlevimi GitHub.Com veya kendi sitemde bulabilirsiniz (github'da Nota, sitemde iki dosya var, işlev ve demo için kaynaklanabilecek 1 dosyada paketlenmiş) kullanın veya sadece demo için çalıştırın)

Numune:

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

myBc işlevi, arka plan görevini basit sözdizimiyle ve tarih için kullanmanıza izin verir:

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

Buradan, arka plan işlemlerinden birini bitirmek istiyorsanız, onun fd işlevini kapatmanız yeterlidir:

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

bu gerekli değildir, çünkü ana işlem bittiğinde hepsi kapanır.

59
F. Hauri

Yapmanın üç yolunu biliyorum:

1) İşlevler bu tür görevler için uygundur:

func (){
ls -l
}

func diyerek onu çağır

2) Başka bir uygun çözüm de olabilir:

var="ls -l"
eval $var

3) Üçüncüsü doğrudan değişkenleri kullanıyor:

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

Üçüncü çözümün çıktısını iyi bir şekilde elde edebilirsiniz:

echo "$var"

ve ayrıca pis bir şekilde:

echo $var
52
MLSC

Sadece farklı olmak için:

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

Çok satırlı/çoklu komut/s ile yapmak istiyorsanız o zaman bunu yapabilirsiniz:

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

Veya:

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

Örnek:

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

Çıktı:

first
second
third

heredoc komutunu kullanarak, uzun tek satırlı kodunuzu çok satırlı koddan ayırarak işleri kolayca basitleştirebilirsiniz. Başka bir örnek:

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

Bir değişkeni ayarlarken NO - - ve/veya sonra - işaretlerinden sonra olduğundan emin olun. Kelimenin tam anlamıyla bunu anlamaya çalışarak bir saat harcadı, her türlü çözümü denedi! Bu hoş değil.

Doğru:

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

Başarısız Olacak hatayla: (malzeme: bulunamadı veya benzeri değil)

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

Ya da kullanmanız gerekir

$(command-here)

veya

`command-here`

örnek

#!/bin/bash

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

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

echo "$MOREF"
8
Diego Velez

Bu, oluşturduğunuz her karmaşık kodu doğru şekilde vurgulayamayan bazı metin editörleriyle birlikte kullanmak için iyi bir yoldur.

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

Geri keneleri (aksan mezarları olarak da bilinir) veya $() kullanabilirsiniz. Gibi

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

Her ikisi de aynı etkiye sahiptir. Ancak OUTPUT = $ (x + 2) daha okunaklı ve en yenisidir.

6
Pratik Patil

Bazıları bunu faydalı bulabilir. Hilenin $(()) çift köşeli parantez kullandığı değişken yerine tamsayı değerleri:

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

İşte iki yol daha:

Lütfen boşluğun bash içinde çok önemli olduğunu unutmayın. Bu nedenle, komutunuzun çalışmasını istiyorsanız, daha fazla boşluk bırakmadan olduğu gibi kullanın.

  1. aşağıdaki L harshil atar ve sonra yazdırır

    L=$"harshil"
    echo "$L"
    
  2. aşağıdaki, tr komutunun çıktısını L2'ye atar. tr, başka bir L1 değişkeninde çalıştırılıyor.

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

Çalıştırmaya çalıştığınız komut başarısız olursa, çıktıyı hata akışına yazar ve ardından konsola yazdırılır. Bunu önlemek için, hata akışını yönlendirmeniz gerekir

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