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)
export 3
dapat berubah$3
menjadi variabel lingkungan. Kamu tidak bisaunset 3
; dan Anda tidak dapat menetapkan$3
nilai baru menggunakan3=val
.Jawaban:
Variabel adalah salah satu dari tiga varietas parameter dalam shell.
_
atau huruf, diikuti dengan nol atau lebih huruf, angka atau_
.$1
,$2
, ...$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
export
atribut pada nama yang valid.sumber
set
menampilkan semua parameter dalamzsh
(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 set
akan mencetak1=foo
)Variabel Lingkungan vs Parameter Posisi
Sebelum kita mulai membahas
$INTEGER
jenis variabel, kita perlu memahami apa sebenarnya mereka dan bagaimana mereka berbeda dari variabel lingkungan. Variabel seperti$INTEGER
disebut parameter posisi. Ini dijelaskan dalam standar POSIX (Portable Operating System Interface), bagian 2.1 (penekanan tambang):Sebaliknya, variabel seperti
$HOME
dan$PATH
adalah variabel lingkungan. Definisi mereka dijelaskan di bagian 8 dari standar :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 melakukannyaecho 'Hello' 'World'
, itu akan mencetakHello
danWorld
string, karena ini adalah parameter posisi untukecho
- hal-hal yang ingin Andaecho
operasikan. Danecho
dibangun 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 apaHello
danWorld
karena 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
HOME
atauPATH
memiliki format spesifik, makna spesifik, dan mereka akan berarti sama untuk setiap program.HOME
variabel akan berarti sama dengan utilitas eksternal seperti/usr/bin/find
atau 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, misalnyaUID
variabel 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-terminal
mulai akan melihatSHELL
variabel 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 keksh
- gnome-terminal kemudian akan menelurkanksh
bukanbash
. Itu juga contoh bagaimana lingkungan digunakan oleh program. Jika saya secara eksplisit memintagnome-terminal
dengan-e
menjalankan 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
main
fungsi C yang biasanya terlihat, di mana
argc
ada sejumlah argumen baris perintah danargv
secara efektif array parameter baris perintah, dan kemudian adaenviron
fungsi (di Linux ituman -e 7 environ
) untuk mengakses hal-hal seperti jalur direktori home pengguna, daftar direktori diPATH
mana kita dapat mencari executable, dll. Script Shell juga dimodelkan dengan cara yang sama. Dalam terminologi shell, kami memiliki parameter posisi$1
,$2
dan 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
bash
shell login adalah satu "... yang karakter pertama argumen nol adalah -, atau yang dimulai dengan opsi --login." ( Referensi ) )Sebaliknya, skrip dan shell yang diluncurkan dengan
-c
opsi dapat memanfaatkan$1
dan$2
argumen. Contohnya,Perhatikan bahwa saya juga pernah menggunakannya di
sh
sana, karena kekhasan kecil dari-c
opsi 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
bash
dengan parameter posisinya sendiri, tetapi parameter posisional itu menjadi parameter untukecho
danstat
. Dan setiap program memahaminya dengan caranya sendiri. Jika kami berikan kestat
stringHello World
dan tidak ada fileHello World
itu akan menghasilkan kesalahan;bash
memperlakukannya hanya sebagai string sederhana tetapistat
mengharapkan string itu menjadi nama file yang ada. Sebaliknya, semua program akan setuju bahwa variabel lingkunganHOME
adalah 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:
Kami juga dapat menempatkan variabel ke lingkungan hanya menggunakan
export variable=value
dari dalam shell atau skrip. Atau kita dapat menjalankan perintah dengan lingkungan yang benar-benar kosongenv -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
shift
perintah.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
set
perintah, menurut berperilaku pengguna seperti begitu (dari manual bash, penekanan ditambahkan):Dengan kata lain,
set
lihat variabel spesifik untuk shell, beberapa di antaranya terjadi di lingkungan, misalnyaHOME
. Sebaliknya, perintah sukaenv
danprintenv
lihat variabel lingkungan aktual yang digunakan perintah. Lihat juga ini .sumber
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.$1
,$2
dan sebagainya di shell interaktif, dan pada kenyataannya itu dilakukan denganset
perintah, sering untuk mengatasi/bin/sh
keterbatasan 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.conda
, ketika Anda menjalankansource 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.conda
pengembang 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, sementarabash
sebenarnya memiliki variabel lingkungan untuk tujuan ituThe
$1, $2, $3, ..., ${10}, ${11}
variabel disebut parameter posisi dan akan dibahas dalam bagian bash manual3.4.1
Adapun
$?
dan$0
, parameter khusus ini dibahas pada bagian selanjutnya3.4.2
sumber
$1
,$2
... adalah parameter posisi , bukan variabel, apalagi variabel lingkungan.Dalam Bourne-seperti terminologi shell,
$something
disebut 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:
$foo
,$PATH
$1
,$2
... argumen yang diterima skrip Anda)$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.
akan mengirimkan
VAR
variabel lingkungan keprintenv
perintah (yang kami kirim untuk mencetak kontennya), tetapi Anda juga dapat melakukannya:atau:
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: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
1
variabel lingkungan ke perintah:Itu tidak berarti bahwa memanggil shell dengan variabel lingkungan itu akan menetapkan nilai
$1
parameter:sumber
Tidak, ini adalah parameter skrip. Misalnya jika Anda memanggil skrip Anda seperti:
kemudian di dalam skrip, akan ada parameter ini tersedia sebagai
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).
sumber
$0
akan menunjuk ke proses terminal Anda saat ini (kemungkinan bash) dan$?
hanyalah kode keluar dari proses terakhir.gnome-terminal
dengan argumen (gnome-terminal Hello World
). Saya bisa melihat$0
, tetapi saya tidak bisa melihat$1
dan$2
.