Bagaimana saya bisa menetapkan output dari perintah ke variabel shell?

76

Saya ingin menetapkan hasil ekspresi ke variabel dan menyatukannya dengan string, lalu menggemakannya. Inilah yang saya punya:

#!/bin/bash
cd ~/Desktop;
thefile= ls -t -U | grep -m 1 "Screen Shot";
echo "Most recent screenshot is: "$thefile;

Tapi itu menghasilkan:

Screen Shot 2011-07-03 at 1.55.43 PM.png
Most recent screenshot is: 

Jadi, sepertinya itu tidak ditugaskan $thefile, dan sedang dicetak saat dijalankan.

Nathan G.
sumber

Jawaban:

108

Tugas shell adalah kata tunggal, tanpa spasi setelah tanda sama dengan. Jadi, apa yang Anda tulis memberikan nilai kosong ke thefile; lebih jauh, karena tugas dikelompokkan dengan perintah, itu membuat thefilevariabel lingkungan dan tugas adalah lokal untuk perintah tertentu, yaitu hanya panggilan untuk lsmelihat nilai yang ditugaskan.

Anda ingin menangkap output dari suatu perintah, jadi Anda perlu menggunakan substitusi perintah :

thefile=$(ls -t -U | grep -m 1 "Screen Shot")

(Beberapa literatur menunjukkan sintaks alternatif thefile=`ls …`; sintaks backquote setara dengan sintaks dolar-kurung kecuali bahwa mengutip dalam backquotes kadang-kadang aneh, jadi gunakan saja $(…).)

Keterangan lain tentang skrip Anda:

  • Menggabungkan -t(urutkan berdasarkan waktu) dengan -U(jangan urutkan) tidak masuk akal; gunakan saja -t.
  • Daripada menggunakan grepuntuk mencocokkan tangkapan layar, lebih jelas untuk meneruskan wildcard ke lsdan gunakan headuntuk mengambil file pertama:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
  • Ini umumnya ide yang buruk untuk mengurai outputls . Ini bisa gagal sangat buruk jika Anda memiliki nama file dengan karakter yang tidak dapat dicetak. Namun, menyortir file berdasarkan tanggal sulit tanpa ls, jadi ini solusi yang dapat diterima jika Anda tahu Anda tidak akan memiliki karakter yang tidak diinginkan atau garis miring terbalik dalam nama file.

  • Selalu gunakan tanda kutip ganda di sekitar substitusi variabel , yaitu di sini tulis

    echo "Most recent screenshot is: $thefile"

    Tanpa tanda kutip ganda, nilai variabel diekspansi ulang, yang akan menyebabkan masalah jika berisi spasi putih atau karakter khusus lainnya.

  • Anda tidak perlu titik koma di akhir baris. Mereka mubazir tetapi tidak berbahaya.
  • Dalam skrip shell, sering kali ide yang baik untuk dimasukkan set -e. Ini memberi tahu shell untuk keluar jika ada perintah yang gagal (dengan mengembalikan status bukan nol).

Jika Anda memiliki GNU find (khususnya jika Anda menjalankan Linux atau Cygwin yang tidak tertanam), ada pendekatan lain untuk menemukan file terbaru: miliki finddaftar file dan tanggalnya, dan gunakan sortdan tailuntuk mengekstrak file termuda.

thefile=$(find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@ %p" |
          sort -k 1n | tail -n 1)

Jika Anda ingin menulis skrip ini di zsh daripada bash, ada cara yang jauh lebih mudah untuk menangkap file terbaru, karena zsh memiliki kualifikasi global yang memungkinkan kecocokan wildcard tidak hanya pada nama tetapi juga pada metadata file. Bagian (om[1])setelah polanya adalah kualifikasi bola dunia; ommengurutkan kecocokan dengan bertambahnya usia (yaitu dengan waktu modifikasi, yang terbaru lebih dulu) dan [1]mengekstrak kecocokan pertama saja. Seluruh kecocokan harus dalam tanda kurung karena secara teknis ini adalah array, karena globbing mengembalikan daftar file, bahkan jika itu [1]berarti bahwa dalam kasus khusus ini daftar berisi (paling banyak) satu file.

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"
Gilles
sumber
7
Wow! Info lebih dari yang saya bisa harapkan. Terima kasih banyak atas jawaban Anda; Saya sangat menghargai semua upaya yang Anda lakukan untuk ini! Anda baru saja menunjukkan kepada saya bahwa saya benar-benar harus belajar banyak. Saya akan pergi untuk membeli buku tentang bash: P.
Nathan G.
3
Itulah yang saya sebut jawaban pasti ! :)
alex
4

Jika Anda ingin melakukannya dengan multiline / beberapa perintah / s 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
Jahid
sumber
Ini jawaban yang bagus. apakah ada cara saya bisa menentukan variabel sebagai bagian dari perintah? misalnyaoutput=$(echo $someVariable)
Zach Smith