Saya memiliki perintah kompleks yang ingin saya buat skrip shell / bash. Saya bisa menulisnya $1
dengan mudah:
foo $1 args -o $1.ext
Saya ingin dapat memberikan beberapa nama input ke skrip. Apa cara yang benar untuk melakukannya?
Dan, tentu saja, saya ingin menangani nama file dengan spasi di dalamnya.
bash
command-line
Thelema
sumber
sumber
Jawaban:
Gunakan
"$@"
untuk mewakili semua argumen:Ini akan mengulangi setiap argumen dan mencetaknya pada baris yang berbeda. $ @ berperilaku seperti $ * kecuali bahwa ketika dikutip argumen dipecah dengan benar jika ada spasi di dalamnya:
sumber
"$@"
adalah ia tidak berkembang ketika tidak ada parameter posisi sedangkan"$*"
ekspansi ke string kosong - dan ya, ada perbedaan antara tidak ada argumen dan satu argumen kosong. Lihat${1:+"$@"} in
/ bin / sh` .$var
Saya bisa mengeksekusi argumen.shift
membuang$1
, dan menggeser semua elemen berikutnya.Tulis ulang jawaban yang sekarang dihapus oleh VonC .
Jawaban singkat Robert Gamble berkaitan langsung dengan pertanyaan itu. Yang ini menguatkan pada beberapa masalah dengan nama file yang mengandung spasi.
Lihat juga: $ {1: + "$ @"} di / bin / sh
Tesis dasar:
"$@"
benar, dan$*
(tidak dikutip) hampir selalu salah. Ini karena"$@"
berfungsi dengan baik ketika argumen berisi spasi, dan berfungsi sama seperti$*
ketika mereka tidak. Dalam beberapa keadaan,"$*"
juga OK, tetapi"$@"
biasanya (tetapi tidak selalu) bekerja di tempat yang sama. Tidak dikutip,$@
dan$*
setara (dan hampir selalu salah).Jadi, apa perbedaan antara
$*
,$@
,"$*"
, dan"$@"
? Mereka semua terkait dengan 'semua argumen ke shell', tetapi mereka melakukan hal-hal yang berbeda. Ketika dikutip,$*
dan$@
lakukan hal yang sama. Mereka memperlakukan setiap 'kata' (urutan non-spasi putih) sebagai argumen terpisah. Bentuk-bentuk yang dikutip sangat berbeda, meskipun:"$*"
memperlakukan daftar argumen sebagai string yang dipisahkan oleh spasi, sedangkan"$@"
memperlakukan argumen hampir persis seperti ketika ditentukan pada baris perintah."$@"
tidak berkembang sama sekali ketika tidak ada argumen posisi;"$*"
meluas ke string kosong - dan ya, ada perbedaan, meskipun sulit untuk melihatnya. Lihat informasi lebih lanjut di bawah ini, setelah pengenalan perintah (non-standar)al
.Tesis sekunder: jika Anda perlu memproses argumen dengan spasi dan kemudian meneruskannya ke perintah lain, maka Anda terkadang memerlukan alat yang tidak standar untuk membantu. (Atau Anda harus menggunakan array, dengan hati-hati:
"${array[@]}"
berperilaku analog dengan"$@"
.)Contoh:
Mengapa itu tidak berhasil? Itu tidak berfungsi karena shell memproses tanda kutip sebelum memperluas variabel. Jadi, untuk mendapatkan shell agar memperhatikan kutipan yang tertanam di dalamnya
$var
, Anda harus menggunakaneval
:Ini menjadi sangat sulit ketika Anda memiliki nama file seperti "
He said, "Don't do this!"
" (dengan tanda kutip dan tanda kutip ganda dan spasi).Kerang (semuanya) tidak membuatnya sangat mudah untuk menangani hal-hal seperti itu, jadi (cukup lucu) banyak program Unix tidak melakukan pekerjaan yang baik untuk menanganinya. Pada Unix, nama file (komponen tunggal) dapat berisi karakter apa pun kecuali slash dan NUL
'\0'
. Namun, shell sangat tidak mendorong spasi atau baris baru atau tab di mana pun dalam nama jalur. Itu juga mengapa nama file Unix standar tidak mengandung spasi, dll.Ketika berhadapan dengan nama file yang mungkin mengandung spasi dan karakter merepotkan lainnya, Anda harus sangat berhati-hati, dan saya telah lama mengetahui bahwa saya memerlukan program yang tidak standar pada Unix. Saya menyebutnya
escape
(versi 1.1 bertanggal 1989-08-23T16: 01: 45Z).Berikut adalah contoh
escape
penggunaan - dengan sistem kontrol SCCS. Ini adalah skrip sampul yang melakukan adelta
(think check-in ) dan aget
(think check-out ). Berbagai argumen, terutama-y
(alasan mengapa Anda melakukan perubahan) akan berisi baris kosong dan baru. Perhatikan bahwa skrip berasal dari tahun 1992, sehingga menggunakan back-ticks sebagai ganti$(cmd ...)
notasi dan tidak digunakan#!/bin/sh
pada baris pertama.(Saya mungkin tidak akan menggunakan escape dengan sangat teliti akhir-akhir ini - itu tidak diperlukan dengan
-e
argumen, misalnya - tetapi secara keseluruhan, ini adalah salah satu skrip saya yang lebih sederhana menggunakanescape
.)The
escape
Program hanya output argumen, agak sepertiecho
tidak, tetapi memastikan bahwa argumen dilindungi untuk digunakan denganeval
(satu tingkat darieval
; Saya punya sebuah program yang melakukan terpencil eksekusi shell, dan yang diperlukan untuk melarikan diri outputescape
).Saya memiliki program lain yang disebut
al
yang daftar argumennya satu per baris (dan itu bahkan lebih kuno: versi 1.1 tanggal 1987-01-27T14: 35: 49). Ini paling berguna ketika men-debug skrip, karena dapat dicolokkan ke baris perintah untuk melihat argumen apa yang sebenarnya diteruskan ke perintah.[ Ditambahkan: Dan sekarang untuk menunjukkan perbedaan antara berbagai
"$@"
notasi, berikut adalah satu contoh lagi:Perhatikan bahwa tidak ada yang mempertahankan kosong asli antara
*
dan*/*
pada baris perintah. Juga, perhatikan bahwa Anda dapat mengubah 'argumen baris perintah' di shell dengan menggunakan:Ini menetapkan 4 opsi, '
-new
', '-opt
', 'and
', dan 'arg with space
'.]
Hmm, itu jawaban yang cukup panjang - mungkin penafsiran adalah istilah yang lebih baik. Kode sumber
escape
tersedia berdasarkan permintaan (email ke firstname dot lastname di gmail dot com). Kode sumberal
sangat sederhana:Itu saja. Ini setara dengan
test.sh
skrip yang diperlihatkan oleh Robert Gamble, dan dapat ditulis sebagai fungsi shell (tetapi fungsi shell tidak ada di versi lokal Bourne shell ketika saya pertama kali menulisal
).Perhatikan juga bahwa Anda dapat menulis
al
sebagai skrip shell sederhana:Persyaratan diperlukan sehingga tidak menghasilkan output ketika melewati tidak ada argumen. The
printf
perintah akan menghasilkan baris kosong dengan hanya argumen format string, tetapi program C menghasilkan apa-apa.sumber
Perhatikan bahwa jawaban Robert benar, dan itu juga berfungsi
sh
. Anda dapat (dengan mudah) menyederhanakannya lebih jauh:setara dengan:
Yaitu, Anda tidak perlu apa-apa!
Pengujian (
$
adalah perintah prompt):Saya pertama kali membaca tentang ini di Lingkungan Pemrograman Unix oleh Kernighan dan Pike.
Dalam
bash
,help for
dokumentasikan ini:sumber
"$@"
, dan begitu Anda tahu apafor i
artinya, itu tidak lebih mudah dibaca daripadafor i in "$@"
.for i
itu lebih baik karena menghemat penekanan tombol. Saya membandingkan keterbacaanfor i
danfor i in "$@"
.Untuk kasus sederhana, Anda juga dapat menggunakan
shift
. Ini memperlakukan daftar argumen seperti antrian. Masing-masingshift
membuang argumen pertama dan indeks masing-masing argumen yang tersisa dikurangi.sumber
shift
dalam for for loop, elemen itu masih merupakan bagian dari array, sedangkan dengan inishift
berfungsi seperti yang diharapkan.echo "$1"
untuk menjaga jarak dalam nilai string argumen. Juga, (masalah kecil) ini destruktif: Anda tidak dapat menggunakan kembali argumen jika Anda sudah memindahkan semuanya. Seringkali, itu bukan masalah - Anda hanya memproses daftar argumen sekali saja.--file myfile.txt
Jadi, $ 1 adalah parameter, $ 2 adalah nilai dan Anda memanggil shift dua kali ketika Anda perlu melewati argumen lainAnda juga dapat mengaksesnya sebagai elemen array, misalnya jika Anda tidak ingin mengulanginya semua
sumber
./my-script.sh param1 "param2 is a quoted param"
: - using argv = ($ @) Anda akan mendapatkan [param1, param2], - menggunakan argv = ("$ @"), Anda akan mendapatkan [param1, param2 adalah param yang dikutip]sumber
[ $# != 0 ]
ini lebih baik. Di atas,#$
harus$#
seperti dalamwhile [[ $# > 0 ]] ...
Memperkuat jawaban baz, jika Anda perlu menyebutkan daftar argumen dengan indeks (seperti untuk mencari kata tertentu), Anda dapat melakukan ini tanpa menyalin daftar atau mengubahnya.
Katakanlah Anda ingin membagi daftar argumen di tanda hubung ganda ("-") dan meneruskan argumen sebelum tanda hubung ke satu perintah, dan argumen setelah tanda hubung ke yang lain:
Hasilnya akan terlihat seperti ini:
sumber
getopt Gunakan perintah dalam skrip Anda untuk memformat opsi atau parameter baris perintah.
sumber