Pertanyaan Bagaimana cara menetapkan variabel ke output dari perintah di Bash?


Saya memiliki skrip yang cukup sederhana yaitu sesuatu seperti berikut:

#!/bin/bash

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

echo $MOREF

Ketika saya menjalankan skrip ini dari baris perintah dan memberikan argumennya, saya tidak mendapatkan output apa pun. Namun, ketika saya menjalankan perintah yang terkandung dalam $MOREF variabel, saya bisa mendapatkan output.

Saya ingin tahu bagaimana seseorang dapat mengambil hasil dari sebuah perintah yang harus dijalankan dalam skrip, menyimpannya ke variabel, dan kemudian menampilkan variabel itu di layar?


1085
2018-01-10 20:58


asal


Jawaban:


Selain backticks, Anda bisa menggunakannya $(), yang saya temukan lebih mudah dibaca, dan memungkinkan untuk bersarang.

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

Mengutip (") penting untuk mempertahankan nilai multi-garis.


1661
2018-01-10 21:04



Memperbarui (2018): cara yang benar adalah

$(sudo run command)

Anda menggunakan jenis tanda yang salah. Anda butuh `tidak '. Karakter ini disebut "backticks" (atau "grave accent").

Seperti ini:

#!/bin/bash

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

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

echo "$MOREF"

200
2018-01-10 21:00



Seperti yang telah mereka tunjukkan kepada Anda, Anda harus menggunakan 'backticks'.

Alternatif yang diusulkan $(command) bekerja dengan baik, dan itu juga lebih mudah dibaca, tetapi perhatikan bahwa itu hanya berlaku dengan bash atau korn shell (dan cangkang yang berasal dari mereka), jadi jika skrip Anda harus benar-benar portabel di berbagai sistem Unix, Anda sebaiknya memilih notasi backticks lama.


57
2018-01-11 22:14



Saya tahu tiga cara untuk melakukannya:

1) Fungsi cocok untuk tugas-tugas seperti itu:

func (){
ls -l
}

Memanggilnya dengan mengatakan func

2) Juga solusi lain yang cocok bisa menjadi eval:

var="ls -l"
eval $var

3) Yang ketiga menggunakan variabel secara langsung:

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

Anda bisa mendapatkan output dari solusi ketiga dengan cara yang baik:

echo "$var"

dan juga dengan cara jahat:

echo $var

45
2018-02-13 07:31



Beberapa  trik yang saya gunakan untuk mengatur variabel dari perintah

2nd Edit 2018-02-12: Menambahkan cara khusus, lihat di bagian paling bawah ini!

2018-01-25 Edit: tambahkan fungsi sampel (untuk mengisi vars tentang penggunaan disk)

Cara lama dan kompatibel yang sederhana

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

Sebagian besar kompatibel, cara kedua

Seperti bersarang bisa menjadi berat, kurung diimplementasikan untuk ini

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

Sampel bersarang:

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

membaca lebih dari satu variabel (dengan bashisms)

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

Jika saya hanya ingin Bekas nilai:

array=($(df -k /))

kamu bisa lihat larik variabel:

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

Kemudian:

echo ${array[9]}
529020

Tapi saya lebih suka ini:

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

1 read foo hanya akan melewatkan baris header (variabel $foo akan berisi sesuatu seperti Filesystem 1K-blocks Used Available Use% Mounted on)

Contoh fungsi untuk mengisi beberapa variabel:

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

Tidak a: declare garis tidak diperlukan, hanya untuk keterbacaan.

Tentang sudo cmd | grep ... | cut ...

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

(Tolong hindari sia-sia cat! Jadi ini hanya 1 garpu kurang:

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

Semua pipa (|) menyiratkan garpu. Di mana proses lain harus dijalankan, mengakses disk, pustaka panggilan dan sebagainya.

Jadi menggunakan sed untuk sampel, akan membatasi subproses menjadi hanya satu garpu:

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

Dan dengan bashisms:

Tetapi untuk banyak tindakan, kebanyakan pada file kecil,  dapat melakukan pekerjaan itu sendiri:

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

atau

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

Lebih jauh tentang pembelahan variabel...

Silahkan lihat jawaban saya Bagaimana cara membagi string pada pemisah di Bash?

Alternatif: mengurangi garpu dengan menggunakan latar belakang tugas jangka panjang

2nd Edit 2018-02-12: Untuk mencegah beberapa garpu seperti

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

atau

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

Dan karena date dan bc bisa bekerja baris demi baris:

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

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

Kita bisa menggunakannya berjalan lama proses latar belakang untuk membuat pekerjaan berulang-ulang, tanpa harus memulai yang baru garpu untuk setiap permintaan:

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

(tentu saja, FD 5 dan 6 harus tidak digunakan!) ... Dari sana, Anda dapat menggunakan proses ini dengan:

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

Ke dalam suatu fungsi newConnector

Anda mungkin menemukan saya newConnector berfungsi GitHub.Com atau pada situs saya sendiri (Nota on github, ada dua file, di situs saya, fungsi dan demo di-bundle ke dalam 1 file yang bisa diambil untuk digunakan atau hanya dijalankan untuk demo)

Mencicipi:

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

Fungsi itu myBc membiarkan Anda menggunakan tugas latar belakang dengan sintaks yang sederhana, dan untuk tanggal:

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

Dari sana, jika Anda ingin mengakhiri salah satu proses latar belakang, Anda hanya perlu menutupnya 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

yang tidak diperlukan, karena semua fd tutup ketika proses utama selesai.


36
2017-12-20 07:06



Hanya untuk menjadi berbeda:

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

22
2018-01-10 21:07



Jika Anda ingin melakukannya dengan perintah multiline / multiple maka Anda dapat melakukan ini:

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

Atau:

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

Contoh:

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

Keluaran:

first
second
third

Menggunakan heredoc Anda dapat menyederhanakan berbagai hal dengan mudah dengan memecah kode baris tunggal panjang Anda menjadi multiline. Contoh lain:

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

12
2018-06-01 17:38



Saat mengatur variabel pastikan Anda memiliki Tidak ada ruang sebelum dan / atau sesudah = tanda. Secara harfiah menghabiskan satu jam mencoba untuk memikirkan hal ini, mencoba semua jenis solusi! Ini tidak keren.

Benar:

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

Akan gagal dengan kesalahan: (barang: tidak ditemukan atau mirip)

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

10
2017-07-18 11:42



Anda dapat menggunakan back-ticks (juga dikenal sebagai makam aksen) atau $(). Seperti-

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

Keduanya memiliki efek yang sama. Tapi OUTPUT = $ (x + 2) lebih mudah dibaca dan yang terbaru.


5
2018-02-09 08:03



Ini adalah cara lain, bagus untuk digunakan dengan beberapa editor teks yang tidak dapat menyorot dengan benar setiap kode rumit yang Anda buat.

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

3
2018-05-10 21:44