Apa cara yang lebih baik untuk diterapkan print_last_arg
?
#!/bin/sh
print_last_arg () {
eval "echo \${$#}" # this hurts
}
print_last_arg foo bar baz
# baz
(Jika ini, katakanlah, #!/usr/bin/zsh
alih-alih #!/bin/sh
saya tahu apa yang harus dilakukan. Masalah saya adalah menemukan cara yang masuk akal untuk mengimplementasikannya #!/bin/sh
.)
EDIT: Di atas hanyalah contoh konyol. Tujuan saya bukan untuk mencetak argumen terakhir, tetapi untuk memiliki cara untuk merujuk ke argumen terakhir dalam fungsi shell.
EDIT2: Saya minta maaf atas pertanyaan yang tidak jelas. Saya berharap bisa melakukannya dengan benar kali ini.
Jika ini /bin/zsh
bukan /bin/sh
, saya bisa menulis sesuatu seperti ini
#!/bin/zsh
print_last_arg () {
local last_arg=$argv[$#]
echo $last_arg
}
Ekspresi $argv[$#]
adalah contoh dari apa yang saya jelaskan di EDIT pertama saya sebagai cara untuk merujuk ke argumen terakhir dalam fungsi shell .
Karena itu, saya seharusnya menulis contoh asli saya seperti ini:
print_last_arg () {
local last_arg=$(eval "echo \${$#}") # but this hurts even more
echo $last_arg
}
... untuk memperjelas bahwa apa yang saya kejar adalah hal yang kurang mengerikan untuk diletakkan di sebelah kanan penugasan.
Namun, perhatikan bahwa dalam semua contoh, argumen terakhir diakses secara non-destruktif . TKI, mengakses argumen terakhir membuat argumen posisi secara keseluruhan tidak terpengaruh.
sumber
var=$( eval echo \${$#})
keeval var=\${$#}
- keduanya tidak sama.shift
danset -- ...
solusi berbasis mungkin merusak kecuali digunakan dalam fungsi di mana mereka juga tidak berbahaya.eval "var=\${$#}"
jika dilakukan dibandingkan denganvar=${arr[evaled index]}
kecuali itu$#
adalah nilai aman yang dijamin. mengapa menyalin seluruh set lalu menghancurkannya ketika Anda bisa langsung mengindeksnya?Jawaban:
... akan mencetak string yang
the last arg is
diikuti oleh <spasi> , nilai argumen terakhir, dan <newline> tertinggal jika ada setidaknya 1 argumen, atau jika tidak, untuk argumen nol, tidak akan mencetak apa pun.Jika Anda melakukannya:
... maka Anda akan menetapkan nilai argumen terakhir ke variabel shell
$lastarg
jika ada setidaknya 1 argumen, atau Anda tidak akan melakukan apa pun. Either way, Anda akan melakukannya dengan aman, dan itu harus portabel bahkan untuk kamu Olde Bourne shell, saya pikir.Berikut ini satu lagi yang akan bekerja dengan cara yang sama, meskipun itu perlu menyalin seluruh array arg dua kali (dan memerlukan
printf
in$PATH
untuk shell Bourne) :sumber
${1+":"} return
segera - karena siapa yang menginginkannya melakukan sesuatu atau mempertaruhkan efek samping apa pun jika dipanggil tanpa alasan? Matematika cukup untuk hal yang sama - jika Anda yakin Anda dapat memperluas bilangan bulat positif, Anda dapat melakukannyaeval
sepanjang hari.$OPTIND
bagus untuk itu.Berikut cara sederhananya:
(diperbarui berdasarkan titik @ cuonglm bahwa dokumen asli gagal ketika tidak ada argumen; ini sekarang menggemakan baris kosong - ubah perilaku itu dalam
else
klausa jika diinginkan)sumber
echo "$# - 1" | bc
Diberikan contoh pos pembuka (argumen posisi tanpa spasi):
Untuk standarnya
IFS=' \t\n'
, bagaimana dengan:Untuk perluasan yang lebih aman
"$*"
, atur IFS (per @ StéphaneChazelas):Tetapi hal di atas akan gagal jika argumen posisi Anda dapat berisi spasi. Dalam hal ini, gunakan ini sebagai gantinya:
Perhatikan bahwa teknik ini menghindari penggunaan
eval
dan tidak memiliki efek samping.Diuji di shellcheck.net
sumber
$IFS
adalah spasi.IFS=' '
sini hanya untuk memperjelas bahwa itu digunakan untuk ekspansi"$*"
.Meskipun pertanyaan ini baru berumur lebih dari 2 tahun, saya pikir saya akan berbagi opsi yang agak compacter.
Mari kita jalankan
Perluasan parameter shell bash .
Edit
Yang lebih ringkas:
echo "${@: -1}"
(Pikirkan ruang)
Sumber
Diuji pada macOS 10.12.6 tetapi juga harus mengembalikan argumen terakhir pada kebanyakan rasa * nix yang tersedia ...
Sakitnya jauh lebih sedikit
¯\_(ツ)_/¯
sumber
echo "${*: -1}"
yangshellcheck
tidak akan mengeluh.${array:n:m:}
adalah ekstensi. (pertanyaan itu secara eksplisit menyebutkan/bin/sh
)POSIXly:
(Pendekatan ini juga bekerja di shell Bourne lama)
Dengan alat standar lainnya:
(Ini tidak akan bekerja dengan yang lama
awk
, yang tidak memilikiARGV
)sumber
awk
bisa juga menjadi awk tua yang tidak punyaARGV
.awk
pendekatan, atau gunakanfor
loop biasa seperti yang lainnya, atau meneruskannya ke fungsi.Ini harus bekerja dengan setiap shell yang sesuai dengan POSIX dan akan bekerja juga dengan shell Solaris Bourne pre POSIX sebelumnya:
dan ini adalah fungsi berdasarkan pendekatan yang sama:
PS: jangan bilang saya lupa kurung kurawal di sekitar fungsi tubuh ;-)
sumber
in ...
dalam satufor
lingkaran dan tidak ada kurung kurawal di sekitarif
pernyataan yang digunakan sebagai fungsi tubuh. Lihatfor_clause
dancompound_command
di pubs.opengroup.org/onlinepubs/9699919799 .Dari "Unix - Pertanyaan yang Sering Diajukan"
(1)
Jika jumlah argumen bisa nol, maka argumen nol
$0
(biasanya nama skrip) akan ditetapkan$last
. Itulah alasannya jika.(2)
(3)
Untuk menghindari pencetakan baris kosong ketika tidak ada argumen, ganti
echo "$last"
untuk:Jumlah argumen nol dihindari oleh
if [ $# -gt 0 ]
.Ini bukan salinan persis dari apa yang ada di tautan di halaman, beberapa perbaikan ditambahkan.
sumber
Ini harus bekerja pada semua sistem dengan
perl
diinstal (sehingga sebagian besar UNICES):Caranya adalah dengan menggunakan
printf
menambahkan\0
setelah setiap argumen shell dan kemudianperl
's-0
saklar yang menetapkan pemisah rekor ke NULL. Kemudian kita beralih pada input, menghapus\0
dan menyimpan setiap baris yang diakhiri NULL sebagai$l
. TheEND
blok (yang ini apa}{
yang) akan dilaksanakan setelah semua input telah membaca begitu akan mencetak yang terakhir "garis": yang terakhir shell argumen.sumber
sed
,awk
atauperl
maka bisa menjadi informasi yang berharga pula. Anda masih dapat melakukan hal yang sama dengansed -z
untuk versi GNU terbaru.Ini adalah versi yang menggunakan rekursi. Tidak tahu bagaimana ini sesuai dengan POSIX ...
sumber
Konsep sederhana, tanpa aritmatika, tanpa loop, tanpa eval , hanya fungsi.
Ingat bahwa cangkang Bourne tidak memiliki aritmatika (diperlukan eksternal
expr
). Jika ingin mendapatkan aritmatika gratis,eval
pilihan bebas, ini adalah pilihan. Membutuhkan fungsi berarti SVR3 atau lebih tinggi (tidak ada parameter yang ditimpa).Lihat di bawah untuk versi yang lebih kuat dengan printf.
Struktur ini untuk panggilan
printlast
adalah tetap , argumen perlu ditetapkan dalam daftar argumen shell$1
,$2
dll (argumen stack) dan panggilan dilakukan seperti yang diberikan.Jika daftar argumen perlu diubah, tetapkan saja:
Atau buat fungsi yang lebih mudah digunakan (
getlast
) yang bisa memungkinkan argumen umum (tapi tidak secepat, argumen dilewatkan dua kali).Harap dicatat bahwa argumen (dari
getlast
, atau semua yang termasuk dalam$@
printlast) dapat memiliki spasi, baris baru, dll. Tetapi tidak NUL.Lebih baik
Versi ini tidak mencetak
0
ketika daftar argumen kosong, dan menggunakan printf yang lebih kuat (mundur keecho
jika eksternalprintf
tidak tersedia untuk shell lama).Menggunakan EVAL.
Jika shell Bourne lebih tua dan tidak ada fungsi, atau jika karena alasan tertentu menggunakan eval adalah satu-satunya pilihan:
Untuk mencetak nilai:
Untuk menetapkan nilai dalam variabel:
Jika itu perlu dilakukan dalam suatu fungsi, argumen harus disalin ke fungsi:
sumber
eval
?for loop
atauwhile loop
?echo "Hello"
memiliki loop karena CPU harus membaca lokasi memori dalam satu lingkaran. Lagi: kode saya tidak memiliki loop tambahan.for
,while
atauuntil
loop. Apakah Anda melihatfor
while
atauuntil
loop tertulis dalam kode? Tidak ?, maka terimalah fakta, rangkul dan pelajari.Komentar ini menyarankan untuk menggunakan kata yang sangat elegan:
sumber