GNU/Linux >> Belajar Linux >  >> Linux

Mengapa Shell Login 'Sudo -i' Memecah Argumen String Perintah Di Sini-doc?

Dalam urutan lima perintah di bawah ini, semuanya bergantung pada tanda kutip tunggal untuk menyerahkan kemungkinan substitusi variabel ke bash yang disebut shell daripada shell panggilan. Pengguna yang menelepon adalah xx , tetapi shell yang dipanggil akan dijalankan sebagai pengguna yy . Perintah pertama mengganti $HOME dengan nilai shell pemanggil karena shell yang dipanggil bukan shell login. Perintah kedua menggantikan nilai $HOME yang dimuat oleh shell login, jadi itu adalah nilai milik pengguna yy . Perintah ketiga tidak bergantung pada nilai $HOME dan membuat file di direktori home yang ditebak dari pengguna yy .

Mengapa perintah keempat gagal? Tujuannya adalah untuk menulis file yang sama, tetapi mengandalkan variabel $HOME milik pengguna yy untuk memastikan itu benar-benar berakhir di direktori home-nya. Saya tidak mengerti mengapa shell login merusak perilaku perintah here-doc yang diteruskan sebagai string kutipan tunggal statis. Kegagalan perintah kelima memverifikasi bahwa masalah ini bukan tentang substitusi variabel.

[email protected] ~ $ sudo -u yy bash -c 'echo HOME=$HOME'
HOME=/home/xx
[email protected] ~ $ sudo -iu yy bash -c 'echo HOME=$HOME'
HOME=/home/yy
[email protected] ~ $ sudo -u yy bash -c 'cat > /home/yy/test.sh << "EOF"
> script-content
> EOF
> '
[email protected] ~ $ sudo -iu yy bash -c 'cat > $HOME/test.sh << "EOF"
> script-content
> EOF
> '
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOFscript-contentEOF')
[email protected] ~ $ sudo -iu yy bash -c 'cat > /home/yy/test.sh << "EOF"
> script-content
> EOF
> '
bash: warning: here-document at line 0 delimited by end-of-file (wanted `EOFscript-contentEOF')

Perintah ini dikeluarkan pada sistem 64-bit Linux Mint 18.3 Cinnamon, yang didasarkan pada Ubuntu 16.04 (Xenial Xerus).

Pembaruan: Aspek di sini-doc hanya mengaburkan masalah. Berikut adalah penyederhanaan masalahnya:

$ sudo bash -c 'echo 1
> echo 2'
1
2
$ sudo -i bash -c 'echo 1
> echo 2'
1echo 2

Mengapa yang pertama dari dua perintah itu mempertahankan linebreak dan yang kedua tidak? sudo umum untuk kedua perintah, namun tampaknya melarikan diri/memfilter/menginterpolasi secara berbeda tergantung pada apa pun kecuali opsi “-i”.

Jawaban yang Diterima:

Dokumentasi untuk -i menyatakan:

-i (simulasikan login awal) opsi menjalankan shell yang ditentukan oleh entri basis data kata sandi pengguna target sebagai shell login. Ini berarti bahwa file sumber daya khusus login seperti .profile atau .login akan dibaca oleh shell. Jika sebuah perintah ditentukan, perintah itu akan diteruskan ke shell untuk dieksekusi melalui opsi -c shell.

Artinya, itu benar-benar menjalankan shell login pengguna, dan kemudian melewati perintah apa pun yang Anda berikan sudo untuk itu menggunakan -ctidak seperti apa sudo cmd arg arg biasanya tanpa -i pilihan. Biasanya, sudo hanya menggunakan salah satu exec* berfungsi secara langsung untuk memulai proses itu sendiri, tanpa shell perantara dan semua argumen melewati apa adanya.

Dengan -i , ini mengatur lingkungan, menjalankan shell pengguna sebagai shell login, dan merekonstruksi perintah yang Anda minta untuk dijalankan sebagai argumen ke bash -c . Dalam kasus Anda, ini berjalan (katakanlah, kira-kira) /bin/bash -c "bash -c ' ... '" (bayangkan kutipan yang berhasil).

Masalahnya terletak pada bagaimana sudo mengubah perintah yang Anda tulis menjadi sesuatu yang -c dapat menangani , dijelaskan di bagian selanjutnya. Bagian terakhir memiliki beberapa kemungkinan solusi, dan di antaranya adalah beberapa teknik debugging dan verifikasi,

Mengapa ini terjadi?

Saat meneruskan perintah ke -c , perlu beberapa praproses untuk membuatnya melakukan hal yang benar, yang sudo lakukan sebelum menjalankan shell. Misalnya, jika perintah Anda adalah:

sudo -iu yy echo 'three   spaces'

maka spasi tersebut perlu diloloskan agar perintah memiliki arti yang sama (yaitu, agar argumen tunggal tidak dipecah menjadi dua kata). Yang akhirnya dijalankan adalah:

/bin/bash -c 'echo three   spaces'

Mari kita mulai dengan perintah sederhana Anda:

sudo bash -c 'echo 1
echo 2'

Dalam hal ini, sudo mengubah pengguna, lalu menjalankan execvp("bash", ["bash", "-c", "echo 1necho 2"]) (untuk sintaks literal array yang ditemukan).

Terkait:Bagaimana cara mendapatkan penyelesaian bash untuk alias perintah?

Dengan -i :

sudo -i bash -c 'echo 1
echo 2'

alih-alih mengubah pengguna, lalu menjalankan execv("/bin/bash", ["-bash", "-c", "bash -c echo\ 1\necho\ 2"]) , di mana \ sama dengan liter literal dan n adalah jeda baris. Itu lolos dari spasi dan baris baru di perintah utama Anda dengan mendahuluinya dengan garis miring terbalik.

Artinya, ada shell login luar, yang memiliki dua argumen:-c dan seluruh perintah Anda, direkonstruksi menjadi bentuk yang diharapkan dapat dipahami oleh shell dengan benar. Sayangnya, tidak. bash bagian dalam perintah akhirnya mencoba untuk menjalankan:

echo 1
echo 2

di mana garis fisik pertama diakhiri dengan kelanjutan garis (garis miring terbalik diikuti oleh baris baru), yang dihapus seluruhnya. Baris logisnya adalah echo 1echo 2 , yang tidak sesuai dengan keinginan Anda.

Ada argumen bahwa ini adalah cacat di sudo melarikan diri, mengingat perilaku standar pasangan garis miring terbalik-baris baru. Saya pikir seharusnya aman untuk membiarkan mereka lolos di sini.

Hal yang sama terjadi untuk perintah Anda dengan dokumen di sini. Ini berjalan sebagai, kira-kira:

/bin/bash -c 'bash -c cat << "EOF"\012script-content\012EOF\012'

dimana

Linux
  1. Skrip shell Linux:nomor hex ke string biner

  2. Mengubah shell default di Linux

  3. Mengapa perintah dinding Linux tidak menyiarkan argumen string?

  1. Mengapa Ctrl + V tidak menempel di Bash (Linux shell)?

  2. `ssh <host>` adalah shell login, tetapi `ssh <host> <command>` bukan?

  3. Mengapa perintah sudo membutuhkan waktu lama untuk dieksekusi?

  1. Bagaimana Cara Melewati Argumen Baris Perintah Menjadi Skrip Shell?

  2. Mengapa Bashrc Memeriksa Apakah Shell Saat Ini Interaktif?

  3. Mengapa "ls" Membutuhkan Proses Terpisah Untuk Dieksekusi?