Apakah variabel seperti variabel shell / lingkungan $ 0 dan $ 1?

17

Ada variabel dalam shell seperti $0, $1, $2, $?, dll

Saya mencoba untuk mencetak variabel shell dan environment menggunakan perintah berikut:

set

Tetapi variabel-variabel ini tidak ada dalam daftar.

Jadi pada dasarnya variabel-variabel ini tidak dianggap sebagai variabel shell / lingkungan, kan? (walaupun untuk menampilkannya, Anda harus mengawalinya dengan $, seperti yang Anda lakukan dengan variabel shell / environment)

pengguna7681202
sumber
3
Parameter posisi bukan variabel. Anda tidak export 3dapat berubah $3menjadi variabel lingkungan. Kamu tidak bisa unset 3; dan Anda tidak dapat menetapkan $3nilai baru menggunakan 3=val.
Kaz

Jawaban:

25

Variabel adalah salah satu dari tiga varietas parameter dalam shell.

  1. Sebuah variabel adalah parameter yang namanya shell identifier yang valid; dimulai dengan _atau huruf, diikuti dengan nol atau lebih huruf, angka atau _.
  2. The posisional parameter adalah parameter nomor $1, $2, ...
  3. The khusus parameter semua memiliki nama karakter tunggal, dan selain dari $0, mereka semua berbagai karakter tanda baca.

set hanya menampilkan variabel shell.

Subset dari variabel shell adalah variabel lingkungan, yang nilainya diwariskan dari lingkungan saat shell dijalankan, atau dibuat dengan mengatur exportatribut pada nama yang valid.

chepner
sumber
1
Catatan yang setmenampilkan semua parameter dalam zsh(bukan $ 1, $ 2 ... tetapi $ *, $ @) dan fungsi dalam bash dan bosh. Beberapa shell seperti ksh93 dan versi dash keluaran yang lebih lama yang tidak dipetakan ke variabel shell. ( env 1=foo ksh -c setakan mencetak 1=foo)
Stéphane Chazelas
11

Variabel Lingkungan vs Parameter Posisi

Sebelum kita mulai membahas $INTEGERjenis variabel, kita perlu memahami apa sebenarnya mereka dan bagaimana mereka berbeda dari variabel lingkungan. Variabel seperti $INTEGERdisebut parameter posisi. Ini dijelaskan dalam standar POSIX (Portable Operating System Interface), bagian 2.1 (penekanan tambang):

  1. Shell mengeksekusi fungsi (lihat Perintah Definisi Fungsi), built-in (lihat Utilitas Built-In Khusus), file yang dapat dieksekusi, atau skrip, memberikan nama argumen sebagai parameter posisi bernomor 1 hingga n, dan nama perintah (atau dalam kasus fungsi dalam skrip, nama skrip) sebagai parameter posisional bernomor 0 (lihat Pencarian Perintah dan Eksekusi).

Sebaliknya, variabel seperti $HOMEdan $PATHadalah variabel lingkungan. Definisi mereka dijelaskan di bagian 8 dari standar :

Variabel lingkungan yang didefinisikan dalam bab ini memengaruhi operasi beberapa utilitas, fungsi, dan aplikasi. Ada variabel lingkungan lain yang hanya menarik untuk utilitas tertentu. Variabel lingkungan yang berlaku untuk satu utilitas saja didefinisikan sebagai bagian dari deskripsi utilitas.

Perhatikan deskripsi mereka. Parameter posisi dimaksudkan untuk muncul di depan perintah, yaitu command positional_arg_1 positional_arg_2.... Mereka dimaksudkan untuk disediakan oleh pengguna untuk memberi tahu perintah apa yang harus dilakukan secara spesifik. Ketika Anda melakukannya echo 'Hello' 'World', itu akan mencetak Hellodan Worldstring, karena ini adalah parameter posisi untuk echo- hal-hal yang ingin Anda echooperasikan. Dan echodibangun sedemikian sehingga memahami parameter posisi sebagai string yang akan dicetak (kecuali jika mereka salah satu dari bendera opsional seperti -n). Jika Anda melakukan ini dengan perintah yang berbeda mungkin tidak mengerti apa HellodanWorldkarena mungkin mengharapkan nomor. Perhatikan bahwa parameter posisi tidak "diwariskan" - proses anak tidak tahu tentang parameter posisi orang tua kecuali secara eksplisit diteruskan ke proses anak. Seringkali Anda melihat parameter posisi diteruskan dengan skrip pembungkus - yang mungkin memeriksa contoh perintah yang sudah ada atau menambahkan parameter posisi tambahan ke perintah nyata yang akan dipanggil.

Sebaliknya, variabel lingkungan dimaksudkan untuk mempengaruhi banyak program. Mereka adalah variabel lingkungan , karena mereka ditetapkan di luar program itu sendiri (lebih lanjut tentang ini di bawah). Variabel lingkungan tertentu seperti HOMEatau PATHmemiliki format spesifik, makna spesifik, dan mereka akan berarti sama untuk setiap program. HOMEvariabel akan berarti sama dengan utilitas eksternal seperti /usr/bin/findatau shell Anda (dan akibatnya untuk skrip) - itu adalah direktori home dari nama pengguna di mana proses berjalan. Perhatikan bahwa variabel lingkungan dapat digunakan untuk menjelaskan perilaku perintah tertentu, misalnyaUIDvariabel lingkungan dapat digunakan untuk memeriksa apakah skrip berjalan dengan hak akses root atau tidak dan cabang ke tindakan tertentu yang sesuai. Variabel lingkungan dapat diwariskan - proses anak mendapatkan salinan dari lingkungan orang tua. Lihat juga Jika proses mewarisi lingkungan induk, mengapa kita perlu ekspor?

Singkatnya, perbedaan utama adalah bahwa variabel lingkungan ditetapkan di luar perintah dan tidak dimaksudkan untuk bervariasi (biasanya), sedangkan parameter posisi adalah hal-hal yang dimaksudkan untuk diproses oleh perintah dan mereka berubah.


Bukan hanya konsep shell

Yang saya perhatikan dari komentar adalah bahwa Anda mencampur terminal dan shell, dan akan sangat menyarankan Anda membaca tentang terminal nyata yang pada suatu waktu adalah perangkat fisik. Saat ini, "terminal" yang biasanya kita rujuk, jendela dengan latar belakang hitam dan teks hijau sebenarnya adalah perangkat lunak, sebuah proses. Terminal adalah program yang menjalankan shell, sementara shell juga merupakan program tetapi yang membaca apa yang Anda ketik untuk dieksekusi (yaitu, jika shell interaktif; shell non-interaktif adalah skrip dan sh -c 'echo foo'jenis doa). Lebih lanjut tentang kerang di sini .

Ini adalah perbedaan penting, tetapi juga penting untuk mengenali bahwa terminal adalah program dan karena itu mematuhi aturan lingkungan dan parameter posisi yang sama. Saat Anda gnome-terminalmulai akan melihat SHELLvariabel lingkungan Anda , dan menelurkan shell default yang sesuai untuk Anda, kecuali jika Anda menentukan beberapa perintah lain dengan -e. Katakanlah saya berubah shell default saya ke ksh - gnome-terminal kemudian akan menelurkan kshbukan bash. Itu juga contoh bagaimana lingkungan digunakan oleh program. Jika saya secara eksplisit meminta gnome-terminaldengan -emenjalankan shell tertentu - itu akan melakukannya, tetapi itu tidak akan permanen. Sebaliknya, lingkungan seharusnya sebagian besar tidak berubah (lebih lanjut tentang itu nanti).

Jadi seperti yang Anda lihat, variabel lingkungan dan posisi keduanya adalah properti dari suatu proses / perintah, bukan hanya shell. Ketika datang ke skrip shell, mereka juga mengikuti model yang ditetapkan oleh bahasa pemrograman C. Ambil contoh mainfungsi C yang biasanya terlihat

int main(int argc, char **argv)

, di mana argcada sejumlah argumen baris perintah dan argvsecara efektif array parameter baris perintah, dan kemudian ada environfungsi (di Linux itu man -e 7 environ) untuk mengakses hal-hal seperti jalur direktori home pengguna, daftar direktori di PATHmana kita dapat mencari executable, dll. Script Shell juga dimodelkan dengan cara yang sama. Dalam terminologi shell, kami memiliki parameter posisi $1, $2dan sebagainya, sedangkan $#jumlah parameter posisi. Bagaimana dengan $0? Itulah nama executable itu sendiri, yang lagi-lagi dimodelkan dari bahasa pemrograman C - argv[0]akan menjadi nama C Anda "executable". Dan ini cukup benar untuk sebagian besar bahasa pemrograman dan scripting .

Kerang Interaktif vs Non-interaktif

Salah satu hal yang telah saya isyaratkan adalah perbedaan antara cangkang interaktif dan non-interaktif . Prompt tempat Anda mengetik perintah - yang interaktif, berinteraksi dengan pengguna. Sebaliknya ketika Anda memiliki skrip shell atau Anda menjalankan bash -c''itu non-interaktif.

Dan di sinilah perbedaan menjadi penting. Shell yang Anda jalankan sudah merupakan proses, yang muncul dengan parameter posisi (untuk bashshell login adalah satu "... yang karakter pertama argumen nol adalah -, atau yang dimulai dengan opsi --login." ( Referensi ) )

Sebaliknya, skrip dan shell yang diluncurkan dengan -copsi dapat memanfaatkan $1dan $2argumen. Contohnya,

$ bash -c 'echo $1; stat $2' sh 'Hello World' /etc/passwd
Hello World
  File: '/etc/passwd'
  Size: 2913        Blocks: 8          IO Block: 4096   regular file
Device: 801h/2049d  Inode: 6035604     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2017-08-12 14:48:37.125879962 -0600
Modify: 2017-08-12 14:48:37.125879962 -0600
Change: 2017-08-12 14:48:37.137879811 -0600
 Birth: -

Perhatikan bahwa saya juga pernah menggunakannya di shsana, karena kekhasan kecil dari -copsi adalah untuk mengambil parameter posisi pertama dan menetapkannya $0, tidak seperti biasanya menjadi nama program.

Hal lain yang penting untuk diperhatikan adalah bahwa parameter posisi adalah apa yang saya sebut "framable". Perhatikan caranya, kami pertama kali meluncurkan bashdengan parameter posisinya sendiri, tetapi parameter posisional itu menjadi parameter untuk echodan stat. Dan setiap program memahaminya dengan caranya sendiri. Jika kami berikan ke statstring Hello Worlddan tidak ada file Hello Worlditu akan menghasilkan kesalahan; bashmemperlakukannya hanya sebagai string sederhana tetapi statmengharapkan string itu menjadi nama file yang ada. Sebaliknya, semua program akan setuju bahwa variabel lingkungan HOMEadalah direktori (kecuali programmer mengkodekannya dengan cara yang tidak masuk akal).


Bisakah kita dipusingkan dengan variabel lingkungan dan parameter posisi?

Secara teknis, kita bisa dipusingkan dengan keduanya, tetapi kita tidak boleh dipusingkan dengan variabel lingkungan, sementara kita sering harus memberikan parameter posisi. Kita dapat menjalankan perintah di shell dengan menambahkan variabel, misalnya:

$ hello=world bash -c 'echo $hello'
world

Kami juga dapat menempatkan variabel ke lingkungan hanya menggunakan export variable=valuedari dalam shell atau skrip. Atau kita dapat menjalankan perintah dengan lingkungan yang benar-benar kosong env -c command arg1 arg2. Namun, biasanya tidak disarankan untuk dipusingkan dengan lingkungan, terutama menggunakan variabel huruf besar atau menimpa variabel lingkungan yang sudah ada. Perhatikan bahwa itu direkomendasikan meskipun bukan standar.

Untuk parameter posisi, cara untuk mengaturnya jelas, cukup tambahkan mereka ke perintah, tetapi juga ada cara untuk mengaturnya dengan bijaksana , serta mengubah daftar parameter tersebut melalui shiftperintah.

Kesimpulannya, tujuan keduanya berbeda, dan mereka ada karena suatu alasan. Saya harap orang-orang mendapatkan beberapa wawasan dari jawaban ini, dan itu menyenangkan membacanya sama seperti saya menulis jawaban ini.


Catatan pada perintah yang ditetapkan

The setperintah, menurut berperilaku pengguna seperti begitu (dari manual bash, penekanan ditambahkan):

Tanpa opsi, nama dan nilai setiap variabel shell ditampilkan dalam format yang dapat digunakan kembali sebagai input untuk mengatur atau mengatur ulang variabel yang saat ini ditetapkan.

Dengan kata lain, setlihat variabel spesifik untuk shell, beberapa di antaranya terjadi di lingkungan, misalnya HOME. Sebaliknya, perintah suka envdan printenvlihat variabel lingkungan aktual yang digunakan perintah. Lihat juga ini .

Sergiy Kolodyazhnyy
sumber
"Dalam shell interaktif, Anda tidak dapat mereferensikan $ 1, $ 2, dan sebagainya." Adakah referensi langsung untuk ini? Saya telah mengalami kasus aneh di mana ini diatur dalam lingkungan shell interaktif dan saya tidak yakin apakah ini akan dianggap 'non-standar' atau tidak.
user5359531
@ user5359531 Jujur, saya tidak yakin dari mana saya mendapatkannya. Mungkin karena kembali ketika saya memposting jawaban pada tahun 2017 saya mungkin mereferensikan bahwa Anda tidak dapat melakukan sesuatu seperti 1="foo"tetapi kemudian saya menemukan bahwa dengan definisi POSIX sebuah "kata" (itu adalah nama objek seperti variabel atau fungsi) tidak dapat memulai dengan digit (lihat pertanyaan saya diposting pada topik ). Parameter posisi tampaknya merupakan pengecualian untuk aturan ini.
Sergiy Kolodyazhnyy
@ user5359531 Saya telah menghapus bagian dari jawabannya, karena itu tidak terlalu akurat. Anda dapat referensi $1, $2dan sebagainya di shell interaktif, dan pada kenyataannya itu dilakukan dengan setperintah, sering untuk mengatasi /bin/shketerbatasan tidak memiliki array. Terima kasih telah menyampaikan ini kepada saya. Saya juga akan mengedit jawabannya selama beberapa hari ke depan, karena perlu sedikit perbaikan dan pembaruan.
Sergiy Kolodyazhnyy
Terimakasih atas klarifikasinya. Masalah saya memiliki adalah bahwa dengan conda, ketika Anda menjalankan source conda/bin/activate, itu memeriksa apakah $1, $2, dll, ditetapkan, untuk menentukan apakah itu dijalankan sebagai script dengan argumen atau tidak. Ini akhirnya merusak sistem yang memiliki mereka yang diatur dalam lingkungan interaktif karena beberapa alasan. Saya berharap untuk mengetahui apakah perilaku non-standar ini adalah cacat dalam sistem untuk mengatur variabel-variabel ini di lingkungan interaktif, atau pada program untuk menggunakannya untuk menentukan apakah itu dijalankan sebagai skrip.
user5359531
@ user5359531 Saya sarankan Anda mengajukan laporan bug ke condapengembang atau kepada siapa pun yang merupakan penulis asli skrip tersebut, karena memeriksa ${N}parameter jelas merupakan cara yang salah. Ada pertanyaan tentang topik yang sama di sini dan di sini , dan lebih atau kurang cara portabel adalah untuk memeriksa apakah ${0}sama dengan nama skrip, sementara bashsebenarnya memiliki variabel lingkungan untuk tujuan itu
Sergiy Kolodyazhnyy
4

The $1, $2, $3, ..., ${10}, ${11}variabel disebut parameter posisi dan akan dibahas dalam bagian bash manual3.4.1

3.4.1 Parameter Posisi

Parameter posisional adalah parameter yang dilambangkan dengan satu atau lebih digit, selain satu digit 0. Parameter posisional ditetapkan dari argumen shell ketika dipanggil, dan dapat dipindahkan dengan menggunakan perintah set builtin. Parameter posisi N dapat dirujuk sebagai $ {N}, atau sebagai $ N ketika N terdiri dari satu digit. Parameter posisi mungkin tidak ditugaskan dengan pernyataan penugasan. Set dan shift bawaan digunakan untuk mengatur dan menghapusnya (lihat Perintah Shell Builtin). Parameter posisi diganti sementara ketika fungsi shell dijalankan (lihat Fungsi Shell).

Ketika parameter posisi yang terdiri dari lebih dari satu digit diperluas, itu harus dilampirkan dalam kurung.

Adapun $?dan $0, parameter khusus ini dibahas pada bagian selanjutnya3.4.2

3.4.2 Parameter Khusus

Shell memperlakukan beberapa parameter secara khusus. Parameter ini hanya dapat dirujuk; penugasan kepada mereka tidak diizinkan.

...

?

($?) Memperluas status keluar dari pipa latar depan yang baru saja dieksekusi.

0

($ 0) Perluas nama shell script atau shell. Ini diatur pada inisialisasi shell. Jika Bash dipanggil dengan file perintah (lihat Script Shell), $ 0 diatur ke nama file itu. Jika Bash dimulai dengan opsi -c (lihat Meminta Bash), maka $ 0 diatur ke argumen pertama setelah string yang akan dieksekusi, jika ada. Jika tidak, itu diatur ke nama file yang digunakan untuk memanggil Bash, seperti yang diberikan oleh argumen nol.

Jesse_b
sumber
4

$1, $2... adalah parameter posisi , bukan variabel, apalagi variabel lingkungan.

Dalam Bourne-seperti terminologi shell, $somethingdisebut parameter ekspansi (juga mencakup ${something#pattern}dan lebih dalam beberapa kerang seperti ${array[x]}, ${param:offset}, ${x:|y}dan banyak operator ekspansi lebih).

Ada berbagai macam parameter:

  • variabel seperti $foo,$PATH
  • parameter posisi ( $1, $2... argumen yang diterima skrip Anda)
  • parameter khusus lainnya seperti $0, $-, $#, $*, $@, $$, $!, $?...

nama variabel dalam Bourne like shells harus dimulai dengan satu karakter alfabet (yang dikenali oleh lokal, atau terbatas pada a-zA-Z tergantung pada shell) dan garis bawah dan diikuti oleh nol atau lebih karakter alfanumerik atau garis bawah.

Bergantung pada shell, variabel dapat memiliki tipe yang berbeda (skalar, array, hash) atau diberikan atribut tertentu (read-only, diekspor, huruf kecil ...).

Beberapa variabel tersebut dibuat oleh shell atau memiliki arti khusus untuk shell (seperti $OPTIND, $IFS, $_...)

Variabel Shell yang memiliki atribut ekspor secara otomatis diekspor sebagai variabel lingkungan ke perintah yang dijalankan shell.

Variabel lingkungan adalah konsep yang terpisah dari variabel shell. Mengekspor variabel shell bukan satu-satunya cara untuk mengirimkan variabel lingkungan ke eksekusi perintah.

VAR=foo
export VAR
printenv VAR

akan mengirimkan VARvariabel lingkungan ke printenvperintah (yang kami kirim untuk mencetak kontennya), tetapi Anda juga dapat melakukannya:

env VAR=foo printenv VAR

atau:

perl -e '$ENV{VAR}="foo"; exec "printenv", "VAR"'

contohnya.

Variabel lingkungan dapat memiliki nama apa pun (dapat berisi karakter apa pun tetapi =dan bahkan dapat kosong). Itu bukan ide yang baik untuk memberikan nama yang tidak kompatibel dengan nama variabel shell Bourne-like ke variabel lingkungan, tetapi itu mungkin:

$ env '#+%=whatever' printenv '#+%'
whatever

Shell akan memetakan variabel lingkungan yang mereka terima ke variabel shell hanya untuk variabel lingkungan yang namanya variabel shell yang valid (dan dalam beberapa shell mengabaikan beberapa yang khusus seperti $IFS).

Jadi, selagi Anda bisa meneruskan 1variabel lingkungan ke perintah:

$ env '1=whatever' printenv 1
whatever

Itu tidak berarti bahwa memanggil shell dengan variabel lingkungan itu akan menetapkan nilai $1parameter:

$ env '1=whatever' sh -c 'echo "$1"' script-name foo bar
foo
Stéphane Chazelas
sumber
3

Tidak, ini adalah parameter skrip. Misalnya jika Anda memanggil skrip Anda seperti:

mynicescript.sh one two three

kemudian di dalam skrip, akan ada parameter ini tersedia sebagai

$1 = one
$2 = two
$3 = three

dan $ 0 adalah nama skrip itu sendiri.

Jadi ketika Anda berada di luar skrip, variabel-variabel ini tidak tersedia (kecuali $ 0, yang menampilkan / bin / bash - shell itu sendiri).

Jaroslav Kucera
sumber
"Jadi ketika Anda berada di luar skrip, variabel-variabel ini tidak tersedia" Apa yang Anda maksud dengan "di luar skrip" , karena saya bisa melihat nilai-nilai variabel ini di terminal saya.
user7681202
2
@ user7681202: Yang mana yang bisa Anda lihat di terminal Anda? $0akan menunjuk ke proses terminal Anda saat ini (kemungkinan bash) dan $?hanyalah kode keluar dari proses terakhir.
Jesse_b
Saya mencoba menjalankan gnome-terminaldengan argumen ( gnome-terminal Hello World). Saya bisa melihat $0, tetapi saya tidak bisa melihat $1dan $2.
user7681202
@ Jesse_b Terima kasih, saya telah menambahkan konten $ 0 ke dalam jawabannya.
Jaroslav Kucera
1
@ user7681202 Terminal-gnome bukan shell, melainkan terminal emulator (seperti xterm, konsole dll.). Shell berjalan di dalam terminal dan bisa bash / sh / zsh / tcsh dan banyak lagi. Script adalah file yang dapat dieksekusi dengan header yang tepat (seperti #! / Bin / bash) dan konten dapat ditafsirkan oleh shell yang ditentukan dalam mask. Biasanya menggunakan akhiran .sh
Jaroslav Kucera