Bagaimana cara mengirimkan nilai variabel ke stdin dari sebuah perintah?

105

Saya sedang menulis skrip shell yang seharusnya agak aman yaitu tidak melewatkan data aman melalui parameter perintah dan sebaiknya tidak menggunakan file sementara. Bagaimana saya bisa melewatkan variabel ke stdin dari sebuah perintah? Atau, jika tidak memungkinkan, bagaimana cara menggunakan file sementara dengan benar untuk tugas tersebut?

Jonathan Leffler
sumber
Bisakah penyerang berubah $PATH? Sehingga catdapat diganti menjadi/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Jawaban:

74

Sesuatu yang sederhana seperti:

echo "$blah" | my_cmd
Oliver Charlesworth
sumber
7
Waspadai spasi dalam variabel Anda: echo "$blah"lebih baik.
Jonathan Leffler
13
Mari kita lihat bagaimana Anda menangani blah=-n, blah=-e... gunakan printfsebagai gantinya. unix.stackexchange.com/questions/65803/…
Camilo Martin
1
Sepertinya ini akan menjadi rapuh dan rawan kesalahan, bukan?
ThorSummoner
20
6 tahun kemudian, jika saya dapat menghapus jawaban ini saya akan (tetapi saya tidak bisa karena itu telah diterima ...)
Oliver Charlesworth
1
@OliverCharlesworth, mengapa tidak mengeditnya untuk digunakan printf '%s\n' "$blah"? Dengan satu perubahan itu (menghindari banyak jebakan dalam echospesifikasi, yang merupakan jawaban luar biasa Stephane yang menjelaskan secara rinci di Why is printfbetter than echo? Di Unix & Linux , atau bagian PENGGUNAAN APLIKASI dari echospesifikasi yang menyentuh lebih singkat) itu sempurna jawaban yang masuk akal.
Charles Duffy
192

Meneruskan nilai ke stdindalam bash sesederhana:

your-command <<< "$your_variable"

Selalu pastikan Anda memberi tanda kutip di sekitar ekspresi variabel!

Berhati-hatilah, karena ini mungkin hanya bashakan berhasil dan tidak akan berhasil sh.

Martin
sumber
39
Herestrings <<<tidak dijamin akan tersedia, sejauh yang saya tahu, mereka tidak ada di basis POSIX. Mereka akan bekerja seperti yang diharapkan, selama Anda hanya menjalankannya bash. Saya hanya menyebutkannya, karena mereka OP mengatakan 'shell' bukan secara spesifik 'bash'. Meskipun dia menandai pertanyaan itu dengan bash... jadi ini jelas merupakan jawaban yang dapat diterima.
JM Becker
Meskipun demikian, informasi sensitif kemungkinan besar akan lebih mudah bocor jika menggunakan echometode ini karena pembuatan pipa. Perintah herestring akan diproses sepenuhnya oleh bash, meskipun dalam situasi ini (seperti yang disebutkan) Anda sebaiknya tahu bahwa perintah tersebut akan berfungsi pada bash Anda, jika tidak, pesan kesalahan yang dihasilkan karena tidak mendukung herestring juga akan membocorkan informasi tersebut.
Steven Lu
@StevenLu Tunggu, echoadalah built-in. Tidak ada pipa di sana, bukan? Ini Unix, tapi tidak mungkin Unix sebanyak itu .
Camilo Martin
4
@StevenLu printf '%s\n' "$var"memberikan hasil yang sama echo "$var"tetapi tidak akan rusak jika, misalnya var=-en, dan bahkan tidak memerlukan bash.
Camilo Martin
1
Perhatikan juga bahwa baris baru ditambahkan ke string untuk string di sini.
pdr
19

Perhatikan bahwa ' echo "$var" | commandoperasi berarti bahwa input standar terbatas pada baris yang digaungkan. Jika Anda juga ingin terminal terhubung, Anda harus lebih menarik:

{ echo "$var"; cat - ; } | command

( echo "$var"; cat -   ) | command

Ini berarti bahwa baris pertama akan menjadi isinya $vartetapi sisanya akan datang dari catpembacaan input standarnya. Jika perintah tidak melakukan sesuatu yang terlalu mewah (coba aktifkan pengeditan baris perintah, atau jalankan seperti vimitu) maka itu akan baik-baik saja. Jika tidak, Anda harus benar-benar mewah - saya pikir expectatau salah satu turunannya kemungkinan besar akan sesuai.

Notasi baris perintah secara praktis identik - tetapi titik koma kedua diperlukan dengan tanda kurung sedangkan tidak dengan tanda kurung.

Jonathan Leffler
sumber
( echo "$LIST"; cat - ) | sed 1qini berfungsi untuk saya tetapi saya perlu menekan ctrl d ketika saya menjalankan skrip ini?
Gert Cuykens
Iya; yang cat -terus membaca dari keyboard sampai EOF atau interupsi, jadi Anda perlu memberitahukannya EOF dengan mengetikkan control-D.
Jonathan Leffler
apakah ada cara lain untuk mengatasi EOF? sed membutuhkan kucing
Gert Cuykens
Anda tidak perlu menggunakan catsama sekali jika Anda tidak menginginkan input terminal, seperti pada baris pertama. Atau Anda dapat menggunakan catuntuk membuat daftar file. Atau ... Jika Anda ingin perintah membaca input terminal, Anda harus memberitahukannya ketika telah mencapai akhir input. Atau Anda bisa menggunakan ( echo "$var"; sed /quit/q - ) | command; ini berlanjut sampai Anda mengetik baris yang berisi 'keluar'. Anda bisa menjadi inventif tanpa akhir dengan cara Anda menanganinya. Waspadai legenda urban lama dari sebuah program yang berhenti bekerja ketika pengguna mulai bekerja dengan Ekuador. Mereka akan mengetik nama ibu kotanya, Quito, dan program keluar.
Jonathan Leffler
Baiklah jika kau bilang begitu. Tapi kenapa tidak echo "$LIST" | sed 1q | ...? Itu semua tergantung pada Anda. The <<EOFnotasi harus memerlukan EOF pada awal garis di suatu tempat sendiri ke bawah file. Saya tidak akan menggunakannya, saya pikir - tetapi saya masih tidak yakin apa yang ingin Anda capai. Sederhana echo "$LIST" | commandatau echo $LIST | commandmungkin akan cukup dalam praktiknya (dan penting bagi Anda untuk mengetahui pentingnya perbedaan di antara keduanya).
Jonathan Leffler
16

Saya menyukai jawaban Martin, tetapi memiliki beberapa masalah tergantung pada apa yang ada di variabel. Ini

your-command <<< """$your_variable"""

lebih baik jika variabel Anda berisi "atau!

Robert Jacobs
sumber
4
Tapi kenapa? Selain itu, saya tidak dapat mereproduksi masalah apa pun: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"berfungsi dengan baik untuk saya. Apa sebenarnya yang dilakukan ketiganya "? AFAIK Anda hanya melakukan prepending dan menambahkan string kosong.
phk
Cobalah sesuatu yang nyata. Seperti perintah Anda adalah ssh somehost. dan variabel Anda adalah skrip shell.
Robert Jacobs
16
(cat <<END
$passwd
END
) | command

Ini cattidak benar-benar diperlukan, tetapi membantu menyusun kode dengan lebih baik dan memungkinkan Anda untuk menggunakan lebih banyak perintah dalam tanda kurung sebagai masukan untuk perintah Anda.

PoltoS
sumber
Tetapi metode ini memungkinkan Anda untuk melewatkan beberapa baris ke perintah Anda dan juga memungkinkan spasi di$passwd
PoltoS
4
Sejauh ini, ini adalah jawaban terbaik yang tidak membocorkan konten variabel ke pipa atau proses pengintaian.
pengguna2688272
8

Sesuai jawaban Martin, ada fitur bash bernama Here Strings (yang merupakan varian dari fitur Dokumen Di Sini yang lebih banyak didukung ).

http://www.gnu.org/software/bash/manual/bashref.html#Here-Strings

3.6.7 Berikut String

Varian dari dokumen di sini, formatnya adalah:

<<< word

Kata tersebut diperluas dan disuplai ke perintah pada input standarnya.

Perhatikan bahwa Di Sini Strings tampaknya hanya bash, jadi, untuk meningkatkan portabilitas, Anda mungkin akan lebih baik dengan fitur Dokumen Sini asli, sesuai jawaban PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

Atau, varian yang lebih sederhana dari yang di atas:

(cmd <<EOF
$variable
EOF
)

Anda dapat menghilangkan (dan ), kecuali jika Anda ingin ini dialihkan lebih jauh ke perintah lain.

cnst
sumber
5

Cara yang kuat dan portabel ini telah muncul di komentar. Ini harus menjadi jawaban yang berdiri sendiri.

printf '%s' "$var" | my_cmd

atau

printf '%s\n' "$var" | my_cmd

Catatan:

  • Ini lebih baik dari echo, alasannya ada di sini: Mengapa printflebih baik dari echo?
  • printf "$var"salah. Argumen pertama adalah format yang mana berbagai urutan suka %satau \nyang ditafsirkan . Untuk meneruskan variabel ke kanan, itu tidak harus diartikan sebagai format.
  • Biasanya variabel tidak mengandung baris baru. Perintah sebelumnya (dengan %s) meneruskan variabel apa adanya. Namun alat yang bekerja dengan teks mungkin mengabaikan atau mengeluh tentang baris yang tidak lengkap (lihat Mengapa file teks diakhiri dengan baris baru? ). Jadi Anda mungkin menginginkan perintah terakhir (dengan %s\n) yang menambahkan karakter baris baru ke konten variabel. Fakta yang tidak jelas:

    • Di sini string di Bash ( <<<"$var" my_cmd) menambahkan baris baru.
    • Metode apa pun yang menambahkan baris baru menghasilkan stdin yang tidak kosong dari my_cmd, meskipun variabelnya kosong atau tidak ditentukan.
Kamil Maciorowski
sumber
4

Coba ini:

echo "$variable" | command
unbeli
sumber
tetapi bukankah konten $ variabel akan muncul di misalnya, keluaran dari ps -usaat echo dijalankan?
2
tidak itu tidak akan. echoadalah built-in, jadi tidak ada proses untuk ditampilkan di ps
unbeli
2
Waspadai spasi dalam variabel Anda: echo "$variable"lebih baik.
Jonathan Leffler
itu benar, pesta akan memakan ruang ganda, cangkang yang lebih cerdas tidak akan
unbeli
-1

Kerjakan saja:

printf "$my_var" | my_cmd

Jika var tidak mengandung spasi maka tanda kutip dapat dihilangkan.
Jika menggunakan bash, Anda juga dapat melakukan:

echo -n "$my_var" | my_cmd

Hindari menggunakan echo tanpa -n karena akan menyalurkan vraiable dengan linebreak tambahan di akhir.

Clox
sumber
1
tidak berfungsi jika $ my_var adalah %s, printf tidak akan mencetak apa pun. my_var="%s"; printf "$my_var"; - mungkin mencoba printf "%s" "$my_var" | my_cmd ?
hanshenrik