Mengapa mengekspor variabel dalam shell ssh mencetak daftar variabel yang diekspor?

17

Pertimbangkan ini:

$ ssh localhost bash -c 'export foo=bar'
terdon@localhost's password: 
declare -x DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
declare -x HOME="/home/terdon"
declare -x LOGNAME="terdon"
declare -x MAIL="/var/spool/mail/terdon"
declare -x OLDPWD
declare -x PATH="/usr/bin:/bin:/usr/sbin:/sbin"
declare -x PWD="/home/terdon"
declare -x SHELL="/bin/bash"
declare -x SHLVL="2"
declare -x SSH_CLIENT="::1 55858 22"
declare -x SSH_CONNECTION="::1 55858 ::1 22"
declare -x USER="terdon"
declare -x XDG_RUNTIME_DIR="/run/user/1000"
declare -x XDG_SESSION_ID="c5"
declare -x _="/usr/bin/bash"

Mengapa mengekspor variabel dalam bash -csesi berjalan melalui ssh menghasilkan daftar declare -xperintah (daftar variabel yang saat ini diekspor, sejauh yang saya tahu)?

Menjalankan hal yang sama tanpa bash -cmelakukan itu:

$ ssh localhost  'export foo=bar'
terdon@localhost's password: 
$

Juga tidak akan terjadi jika kita tidak export:

$ ssh localhost bash -c 'foo=bar'
terdon@localhost's password: 
$ 

Saya menguji ini dengan sshing dari satu mesin Ubuntu ke yang lain (keduanya menjalankan bash 4.3.11) dan pada mesin Arch, sshing untuk dirinya sendiri seperti yang ditunjukkan di atas (bash versi 4.4.5).

Apa yang terjadi di sini? Mengapa mengekspor variabel di dalam bash -cpanggilan menghasilkan output ini?

terdon
sumber
Ini tidak menjawab pertanyaan, tetapi hasilnya adalah hasil dari menjalankan export. Zsh melakukan hal yang sama.
Stephen Kitt
@StephenKitt ya, saya tahu itu export, saya mencoba memahami apa yang sedang terjadi. Saya akan mengedit untuk menjelaskan bahwa ini hanya terjadi ketika mengekspor.
terdon
Ah OK, saya akan membaca "daftar variabel yang saat ini diekspor, sejauh yang saya tahu" artinya Anda tidak tahu dari mana output itu berasal.
Stephen Kitt
@StephenKitt Maksudku, aku tidak yakin apakah itu setiap variabel yang diekspor atau bagian tertentu atau apa. Oh! Maksudmu itu adalah keluaran dari exportdijalankan sendiri? Itu saya tidak mengerti.
terdon
Catatan yang foo=bartidak muncul dalam daftar.
deltab

Jawaban:

31

Ketika Anda menjalankan perintah ssh, itu dijalankan dengan memanggil Anda $SHELLdengan -cbendera:

-c    If the -c option is present, then commands are read from 
      the first non-option argument command_string.  If there  are
      arguments  after the command_string, the first argument is 
      assigned to $0 and any remaining arguments are assigned to
      the positional parameters.  

Jadi, ssh remote_host "bash -c foo"sebenarnya akan dijalankan:

/bin/your_shell -c 'bash -c foo'

Sekarang, karena perintah yang Anda jalankan ( export foo=bar) berisi spasi dan tidak dikutip dengan benar untuk membentuk keseluruhan, exportdiambil sebagai perintah untuk dijalankan dan sisanya disimpan dalam array parameter posisi. Ini berarti exportdijalankan dan foo=barditeruskan sebagai $0. Hasil akhirnya sama dengan berlari

/bin/your_shell -c 'bash -c export'

Perintah yang benar adalah:

ssh remote_host "bash -c 'export foo=bar'"
Xhienne
sumber
9

ssh menyatukan argumen dengan spasi dan meminta shell login dari pengguna jarak jauh menafsirkannya, jadi di:

ssh localhost bash -c 'export foo=bar'

ssh meminta shell jarak jauh untuk menafsirkan

bash -c export foo=bar

perintah (pada dasarnya, jika host jarak jauh seperti Unix, itu akan menjalankan shell jarak jauh dengan the-shell, -cdan bash -c export foo=barsebagai argumen).

Sebagian besar shell akan menafsirkan bahwa baris perintah sebagai menjalankan bashperintah dengan bash, -c, exportdan foo=barsebagai argumen (jadi lari exportsambil $0berisi foo=bar) saat Anda ingin menjalankannya dengan bash, -cdan export foo=barsebagai argumen.

Untuk itu, Anda harus menggunakan baris perintah seperti:

ssh localhost "bash -c 'export foo=bar'"

(atau:

ssh localhost bash -c \'export foo=bar\'

untuk itu) jadi:

bash -c 'export foo=bar'

baris perintah dilewatkan ke shell jarak jauh. Baris perintah itu akan ditafsirkan oleh sebagian besar shell sebagai menjalankan bashperintah bash, -cdan export foo=barsebagai argumen. Perhatikan bahwa menggunakan

ssh localhost 'bash -c "export foo=bar"'

tidak akan berfungsi jika shell login dari pengguna jarak jauh adalah rcatau esmisalnya di mana "bukan operator mengutip khusus. Kutipan tunggal adalah operator mengutip paling portabel (meskipun ada beberapa variasi tentang bagaimana mereka ditafsirkan antara shell, lihat Bagaimana menjalankan perintah sederhana sewenang-wenang atas ssh tanpa mengetahui shell login dari pengguna jarak jauh? Untuk lebih lanjut tentang itu).

Stéphane Chazelas
sumber