Saya tahu saya bisa menyelesaikan masalah ini dengan beberapa cara, tapi saya bertanya-tanya apakah ada cara untuk melakukannya hanya dengan menggunakan bash built-in, dan jika tidak, apa cara paling efisien untuk melakukannya.
Saya punya file dengan konten seperti
AAA
B C DDD
FOO BAR
maksud saya hanya memiliki beberapa baris dan setiap baris mungkin atau mungkin tidak memiliki spasi. Saya ingin menjalankan perintah seperti
cmd AAA "B C DDD" "FOO BAR"
Jika saya menggunakan cmd $(< file)
saya mengerti
cmd AAA B C DDD FOO BAR
dan jika saya menggunakan cmd "$(< file)"
saya dapatkan
cmd "AAA B C DDD FOO BAR"
Bagaimana cara agar setiap baris diperlakukan tepat satu parameter?
bash
command-substitution
Pro tua
sumber
sumber
Jawaban:
Mudah dibawa:
Atau menggunakan subkulit untuk membuat
IFS
dan opsi mengubah lokal:Shell melakukan pemisahan bidang dan pembuatan nama file pada hasil dari variabel atau perintah substitusi yang tidak dalam tanda kutip ganda. Jadi, Anda perlu mematikan pembuatan nama file
set -f
, dan mengonfigurasi pemisahan bidang denganIFS
hanya membuat baris baru bidang terpisah.Tidak banyak yang bisa diperoleh dengan konstruksi bash atau ksh. Anda dapat membuat
IFS
lokal ke suatu fungsi, tetapi tidakset -f
.Di bash atau ksh93, Anda bisa menyimpan bidang dalam array, jika Anda harus meneruskannya ke beberapa perintah. Anda perlu mengontrol ekspansi pada saat Anda membangun array. Kemudian
"${a[@]}"
mengembang ke elemen array, satu per kata.sumber
Anda bisa melakukan ini dengan array sementara.
Mendirikan:
Isi array:
Gunakan array:
Tidak dapat menemukan cara untuk melakukan itu tanpa variabel sementara - kecuali jika
IFS
perubahan itu tidak pentingcmd
, dalam hal ini:harus melakukannya.
sumber
IFS
selalu membuatku bingung.IFS=$'\n' cmd $(<input)
tidak bekerjaIFS=$'\n'; cmd $(<input); unset IFS
tidak bekerja. Mengapa? Saya kira saya akan menggunakan(IFS=$'\n'; cmd $(<input))
IFS=$'\n' cmd $(<input)
tidak berfungsi karena hanya disetIFS
di lingkungancmd
.$(<input)
diperluas untuk membentuk perintah, sebelum penugasan keIFS
dilakukan.Sepertinya cara kanonik untuk melakukan ini
bash
adalah sesuatu sepertiatau, jika versi bash Anda memiliki
mapfile
:Satu-satunya perbedaan yang dapat saya temukan antara mapfile dan loop sambil membaca versus satu-liner
adalah bahwa yang pertama akan mengonversi baris kosong ke argumen kosong, sedangkan satu-baris akan mengabaikan baris kosong. Dalam hal ini perilaku one-liner adalah apa yang saya inginkan, jadi bonus dua kali lipat menjadi kompak.
Saya akan menggunakan
IFS=$'\n' cmd $(<file)
tetapi tidak berhasil, karena$(<file)
ditafsirkan untuk membentuk baris perintah sebelumIFS=$'\n'
mulai berlaku.Meskipun tidak bekerja dalam kasus saya, saya sekarang sudah belajar bahwa banyak alat mendukung mengakhiri baris dengan
null (\000)
bukannyanewline (\n)
yang tidak membuat banyak ini lebih mudah ketika berhadapan dengan, katakanlah, nama file, yang merupakan sumber umum dari situasi ini :feed daftar nama file yang sepenuhnya memenuhi syarat sebagai argumen untuk MD5 tanpa menggumpal atau interpolasi atau apa pun. Itu mengarah pada solusi non-built-in
Meskipun ini, juga, mengabaikan garis kosong, meskipun tidak menangkap garis yang hanya memiliki spasi putih.
sumber
cmd $(<file)
nilai tanpa mengutip (menggunakan kemampuan bash untuk membagi kata) selalu merupakan taruhan yang berisiko. Jika ada garis*
itu akan diperluas oleh shell ke daftar file.Anda bisa menggunakan bash built-in
mapfile
untuk membaca file menjadi arrayatau, yang belum diuji,
xargs
mungkin melakukannyasumber
xargs
juga tidak membantu.xargs -d
atauxargs -L
-d
pilihan danxargs -L 1
menjalankan perintah sekali per baris tetapi masih membagi argumen di spasi putih.mapfile
sangat berguna bagi saya, karena mengambil baris kosong sebagai item array, yangIFS
metode ini tidak lakukan.IFS
memperlakukan baris baru yang berdekatan sebagai pembatas tunggal ... Terima kasih telah menyajikannya, karena saya tidak mengetahui perintah (meskipun, berdasarkan pada data input OP dan baris perintah yang diharapkan, tampaknya ia benar-benar ingin mengabaikan baris kosong).Cara terbaik yang bisa saya temukan. Hanya bekerja.
sumber
IFS
ruang, tetapi pertanyaannya adalah untuk tidak membagi ruang?Mengajukan
Loop paling dasar (portabel) untuk membagi file pada baris baru adalah:
Yang akan dicetak:
Ya, dengan IFS = standar spacetabnewline.
Mengapa ini berhasil?
IFS
dibutuhkan.set -f
perlu.-r
(mentah) adalah untuk menghindari penghapusan sebagian backslash.Itu tidak akan berhasil jika membelah dan / atau menggumpal diperlukan. Dalam kasus seperti itu dibutuhkan struktur yang lebih kompleks.
Jika Anda perlu (masih portabel) untuk:
IFS= read -r line
IFS=':' read -r a b c
.Membagi file pada beberapa karakter lain (tidak portabel, berfungsi dengan ksh, bash, zsh):
Ekspansi
Tentu saja, judul pertanyaan Anda adalah tentang memisahkan eksekusi perintah pada baris baru untuk menghindari pemisahan spasi.
Satu-satunya cara untuk mendapatkan pemisahan dari shell adalah meninggalkan ekspansi tanpa tanda kutip:
Itu dikontrol oleh nilai IFS, dan, pada ekspansi yang tidak dikutip, globbing juga diterapkan. Untuk membuat itu berhasil, Anda perlu:
Hapus pilihan shell globbing
set +f
:set + f IFS = '' cmd $ (<file)
Tentu saja, itu mengubah nilai IFS dan globbing untuk sisa skrip.
sumber