Bagaimana struktur data $ @ dalam shell?

13

Kami biasanya menggunakan $@untuk mewakili semua argumen kecuali $ 0. Namun, saya tidak tahu apa itu struktur data $@.

Mengapa itu berperilaku berbeda dengan $*ketika termasuk dalam kutipan ganda, adakah yang bisa memberi saya penjelasan tingkat juru?

Itu bisa diulang untuk loop, jadi sepertinya array. Namun, itu juga dapat bergema sepenuhnya dengan sederhana echo $@, jika itu adalah array, hanya elemen pertama yang akan ditampilkan. Karena keterbatasan shell, saya tidak dapat menulis kode percobaan lagi untuk melakukannya.

Perbedaan antara posting ini : Posting ini menunjukkan bagaimana $@berperilaku berbeda dari $*. Tapi saya bertanya-tanya tentang tipe data $@. Shell sebagai bahasa yang menafsirkan, seperti Python, harus mewakili data menurut serangkaian tipe dasar. Atau dengan kata lain, saya ingin tahu bagaimana $ @ disimpan dalam memori komputer.

Apakah itu string, string multi-line atau array?

Jika ini adalah tipe data unik, apakah mungkin untuk mendefinisikan variabel khusus sebagai turunan dari tipe ini?

davmos
sumber
1
Kemungkinan duplikat Apa perbedaan antara $ * dan $ @?
Haxiel
@Haxiel, saya rasa tidak, saya menulis perbedaan mereka di bagian bawah posting saya.
davmos
Anda akan lebih baik dilayani dengan menguji perbedaan output dengan printf '%s\n' "$@"dan printf '%s\n' "$*". The echoutilitas hanya output argumen, tidak peduli apakah mereka satu atau banyak. Keduanya array (dari string), tetapi mereka berperilaku berbeda ketika dikutip ganda. Jika salah satu adalah string multi-line, maka mereka tidak akan dapat menyimpan string multi-line (yang mereka bisa). Tidak jelas masalah apa yang Anda coba selesaikan.
Kusalananda
2
Pertanyaan Anda sama dengan menanyakan @varvariabel dalam Perl, dalam hal penyimpanan yang mendasarinya. Dari sudut pandang program Perl biasa, itu tidak terlalu penting, selain itu dapat diakses sebagai array / daftar (dan fakta bahwa ada konteks di mana daftar diharapkan).
Kusalananda

Jawaban:

16

Itu dimulai sebagai peretasan di shell Bourne. Dalam shell Bourne, pemisahan kata IFS dilakukan (setelah tokenisasi) pada semua kata dalam konteks daftar (argumen baris perintah atau kata-kata forloop loop aktif). Jika Anda memiliki:

IFS=i var=file2.txt
edit file.txt $var

Itu baris kedua akan tokenised di 3 kata, $varakan diperluas, dan membagi + gumpal akan dilakukan pada semua tiga kata, sehingga Anda akan berakhir berjalan eddengan t, f, le.txt, f, le2.txtsebagai argumen.

Mengutip bagian dari itu akan mencegah split + glob. Shell Bourne awalnya ingat karakter mana yang dikutip dengan mengatur bit ke-8 pada mereka secara internal (yang berubah kemudian ketika Unix menjadi 8bit bersih, tetapi shell masih melakukan sesuatu yang mirip dengan mengingat byte mana yang dikutip).

Keduanya $*dan $@merupakan gabungan dari parameter posisi dengan ruang di antaranya. Tetapi ada proses khusus $@ketika di dalam tanda kutip ganda. Jika $1terkandung foo bardan $2terkandung baz, "$@"akan berkembang ke:

foo bar baz
^^^^^^^ ^^^

(dengan ^s di atas menunjukkan karakter mana yang memiliki set bit ke-8). Di mana ruang pertama dikutip (memiliki set bit ke-8) tetapi bukan yang kedua (yang ditambahkan di antara kata-kata).

Dan itu adalah pemisahan IFS yang menangani pemisahan argumen (dengan asumsi karakter spasi $IFSseperti apa adanya secara default). Itu mirip dengan bagaimana $*diperluas dalam pendahulunya shell Mashey (itu sendiri didasarkan pada shell Thomson, sementara shell Bourne ditulis dari awal).

Itu menjelaskan mengapa di shell Bourne awalnya "$@"akan memperluas ke string kosong, bukan apa-apa sama sekali ketika daftar parameter posisi kosong (Anda harus bekerja dengan itu ${1+"$@"}), mengapa tidak menyimpan parameter posisi kosong dan mengapa "$@"tidak bekerja ketika $IFStidak mengandung karakter spasi.

Tujuannya adalah untuk dapat meneruskan daftar argumen kata demi kata ke perintah lain, tetapi itu tidak berfungsi dengan baik untuk daftar kosong, untuk elemen kosong atau ketika $IFStidak mengandung ruang (dua masalah pertama akhirnya diperbaiki di versi yang lebih baru) ).

Shell Korn (yang menjadi dasar spesifikasi POSIX) mengubah perilaku itu dalam beberapa cara:

  • Pemisahan IFS hanya dilakukan pada hasil ekspansi yang tidak dikutip (bukan pada kata-kata literal seperti editatau file.txtdalam contoh di atas)
  • $*dan $@digabung dengan karakter pertama dari $IFSatau spasi ketika $IFSkosong kecuali bahwa untuk kutipan "$@", bahwa joiner tidak dikutip seperti dalam shell Bourne, dan untuk kutip dikutip "$*"ketika IFSkosong, parameter posisi ditambahkan tanpa pemisah.
  • itu menambahkan dukungan untuk array, dan dengan ${array[@]} ${array[*]}mengingatkan Bourne $*dan $@tetapi mulai pada indice 0 bukannya 1, dan jarang (lebih seperti array asosiatif) yang berarti $@tidak dapat benar-benar diperlakukan sebagai array ksh (bandingkan dengan csh/ rc/ zsh/ fish/ di yashmana $argv/ $*normal) array).
  • Elemen-elemen yang kosong dipertahankan.
  • "$@"ketika $#0 sekarang diperluas ke tidak ada alih-alih string kosong, "$@"berfungsi saat $IFStidak mengandung spasi kecuali saat IFSkosong. Tanda kutip $*tanpa wildcard diperluas ke satu argumen (di mana parameter posisi digabungkan dengan spasi) saat $IFSkosong.

ksh93 memperbaiki beberapa masalah yang tersisa di atas. Dalam ksh93, $*dan $@memperluas ke daftar parameter posisi, dipisahkan terlepas dari nilai $IFS, dan kemudian membagi + globbed + penjepit diperluas dalam konteks daftar, $*bergabung dengan byte pertama (bukan karakter) dari $IFS, "$@"dalam konteks daftar memperluas ke daftar parameter posisi, terlepas dari nilai $IFS. Dalam konteks non-daftar, seperti di var=$@, $@digabung dengan spasi terlepas dari nilai $IFS.

bashArray dirancang setelah ksh. Perbedaannya adalah:

  • tidak ada brace-ekspansi atas ekspansi yang tidak dikutip
  • karakter pertama $IFSalih-alih untuk byte
  • beberapa perbedaan sudut kasus seperti perluasan $*ketika tidak dikutip dalam konteks non-daftar saat $IFSkosong.

Sementara spec POSIX dulunya cukup kabur, sekarang lebih atau kurang menentukan perilaku bash.

Ini berbeda dari array normal di dalam kshatau bashdi dalam:

  • Indeks mulai dari 1 bukannya 0 (kecuali "${@:0}"yang mencakup $0(bukan parameter posisi, dan dalam fungsi memberi Anda nama fungsi atau tidak tergantung pada shell dan bagaimana fungsi itu didefinisikan)).
  • Anda tidak dapat menetapkan elemen secara individual
  • itu tidak jarang, Anda tidak dapat menghapus elemen secara individual
  • shift dapat digunakan.

Di zshatau di yashmana array adalah array normal (tidak jarang, indeks dimulai pada satu seperti di semua shell lain tetapi ksh / bash), $*diperlakukan sebagai array normal. zshmemiliki $argvsebagai alias untuknya (untuk kompatibilitas dengan csh). $*sama dengan $argvatau ${argv[*]}(argumen digabungkan dengan karakter pertama $IFStetapi masih dipisahkan dalam konteks daftar). "$@"suka "${argv[@]}"atau "${*[@]}"}mengalami pemrosesan khusus gaya Korn.

Stéphane Chazelas
sumber
8

Namun, saya tidak tahu apa itu struktur data $@.

Ini adalah parameter khusus yang diperluas ke nilai-nilai parameter posisi ... Tapi itu menarik tentang terminologi.

Kita dapat melihat parameter posisi sebagai bagian dari $@, sehingga ia memiliki sejumlah elemen berbeda ( $1, $2...), yang dapat diakses secara independen dan diberi nama dengan bilangan asli berturut-turut. Itu membuatnya menjadi sesuatu yang biasanya disebut array.

Sintaksnya agak aneh, dan bahkan terbatas. Tidak ada cara untuk memodifikasi elemen tunggal array secara individual. Sebaliknya, semuanya harus ditetapkan sekaligus. (Anda dapat menggunakan set -- "$@" foountuk menambahkan nilai, atau set -- "${@:1:2}" foo "${@:3}"untuk menambahkan nilai di tengah. Tetapi Anda dalam kedua kasus Anda harus menuliskan seluruh daftar yang dihasilkan.)

Mengapa itu berperilaku berbeda dengan $*ketika termasuk dalam kutipan ganda,

Karena mereka didefinisikan untuk berperilaku berbeda.

Namun, itu juga dapat bergema sepenuhnya dengan sederhana echo $@, jika itu adalah array, hanya elemen pertama yang akan ditampilkan.

Jika Anda maksudkan fakta yang a=(foo bar asdf); echo $aakan menghasilkan keluaran yang adil foo, maka ini sebagian besar merupakan kekhasan dari sintaks shell, dan fakta bahwa array bernama ksh-style dibuat lebih lambat daripada parameter posisional dan $@. Plain $aadalah sama ${a[0]}sehingga memiliki makna yang kompatibel dengan nilai skalar tunggal, terlepas dari apakah aarray atau variabel skalar sederhana.

The @tanda mengacu pada seluruh daftar digunakan kembali dengan nama array dalam bahwa "${a[@]}"adalah cara untuk mendapatkan seluruh daftar. Dibandingkan dengan array bernama, dengan $@, kurung dan kurung yang tidak perlu dan nama hanya dilewati.

Atau dengan kata lain, saya ingin tahu bagaimana cara $@tersimpan dalam memori komputer.

Itu tergantung pada implementasinya, Anda harus melihat kode sumber dari shell tertentu yang Anda pedulikan.

Apakah itu string, string multi-line atau array?

Array, kebanyakan. Meskipun berbeda dari array bernama ksh-style, karena mereka dapat memiliki bilangan bulat non-negatif yang sewenang-wenang sebagai indeks, tidak hanya yang berturut-turut sama dengan $@. (Yaitu, array bernama bisa jarang, dan memiliki misalnya indeks 1, 3dan 4, dengan 0dan 2hilang. Itu tidak mungkin dengan parameter posisi.)

Ini bukan string tunggal, karena dapat diperluas ke elemen yang berbeda, dan memanggil elemen garis juga tidak benar, karena variabel reguler apa pun, atau salah satu parameter posisi (elemen $@) juga dapat berisi baris baru.

Jika ini adalah tipe data unik, apakah mungkin untuk mendefinisikan variabel khusus sebagai turunan dari tipe ini?

Tidak. Tapi array bernama mungkin lebih berguna.

ilkkachu
sumber
1
+1. TL: DR $@bukan struktur data, itu salah satu dari beberapa fungsi / operator untuk memperluas struktur data parameter-posisi.
Peter Cordes