list=`ls -a R*`
echo $list
Di dalam skrip shell, perintah gema ini akan mencantumkan semua file dari direktori saat ini dimulai dengan R, tetapi dalam satu baris. Bagaimana saya bisa mencetak setiap item dalam satu baris?
Aku butuh perintah generik untuk semua skenario terjadi dengan ls
, du
, find -type -d
, dll
Jawaban:
Jika output dari perintah berisi beberapa baris, maka kutip variabel Anda untuk mempertahankan baris-baris baru ketika menggema:
Jika tidak, shell akan memperluas dan membagi konten variabel di spasi putih (spasi, baris baru, tab) dan itu akan hilang.
sumber
Alih-alih menempatkan
ls
output dalam variabel secara tidak benar dan kemudianecho
memasukkannya, yang menghilangkan semua warna, gunakanDari
man ls
Saya tidak menyarankan Anda melakukan apa pun dengan output
ls
, kecuali menampilkannya :)Gunakan, misalnya, gumpalan shell dan
for
loop untuk melakukan sesuatu dengan file ...* Ini tidak akan menyertakan direktori saat ini
.
atau induknya..
meskipunsumber
echo "$i"
denganprintf "%s\n" "$i"
(yang tidak akan tersedak jika nama file dimulai dengan "-"). Sebenarnya, sebagian besar waktuecho something
harus digantiprintf "%s\n" "something"
.R
sini, tetapi secara umum, ya. Namun, bahkan untuk penulisan skrip, berusaha selalu mengakomodasi pemimpin-
di jalur menyebabkan masalah: orang berasumsi--
, yang hanya didukung oleh beberapa perintah, menandakan akhir dari opsi. Saya telah melihat difind . [tests] -exec [cmd] -- {} \;
mana[cmd]
tidak mendukung--
. Bagaimanapun, setiap jalan di sana dimulai dengan.
! Tapi itu tidak ada argumen melawan menggantiecho
denganprintf '%s\n'
. Di sini, seseorang dapat mengganti seluruh loop denganprintf '%s\n' R/*
. Bash memilikiprintf
builtin, jadi tidak ada batasan jumlah / panjang argumen.Sambil meletakkannya dalam tanda kutip karena @muru menyarankan akan melakukan apa yang Anda minta, Anda mungkin juga ingin mempertimbangkan untuk menggunakan array untuk ini. Sebagai contoh:
The
IFS=$'\n'
memberitahu bash untuk hanya membagi output pada baris baru characcters o mendapatkan setiap elemen dari array. Tanpa itu, itu akan terpecah pada spasi, jadia file name with spaces.txt
akan ada 5 elemen yang terpisah, bukan satu. Pendekatan ini akan rusak jika nama file / direktori Anda dapat berisi baris baru (\n
). Ini akan menyimpan setiap baris dari output perintah sebagai elemen dari array.Perhatikan bahwa saya juga mengubah gaya lama
`command`
ke$(command)
yang merupakan sintaks yang lebih disukai.Anda sekarang memiliki array yang disebut
$dirs
, masing-masing elemen yang merupakan garis dari output dari perintah sebelumnya. Sebagai contoh:Sekarang, karena beberapa keanehan bash (lihat di sini ), Anda harus mengatur ulang
IFS
kembali ke nilai asli setelah melakukan ini. Jadi, simpan dan tetapkan kembali:Atau lakukan secara manual:
Atau, tutup saja terminal saat ini. Yang baru Anda akan memiliki IFS asli diatur lagi.
sumber
du
membuatnya terlihat OP lebih tertarik untuk mempertahankan format output.Jika Anda harus menyimpan output dan Anda menginginkan sebuah array,
mapfile
membuatnya mudah.Pertama, pertimbangkan jika Anda perlu menyimpan output perintah Anda sama sekali. Jika tidak, jalankan saja perintahnya.
Jika Anda memutuskan ingin membaca output dari suatu perintah sebagai array dari baris, memang benar bahwa salah satu cara untuk melakukannya adalah dengan menonaktifkan globbing, mengatur
IFS
untuk membagi pada baris, menggunakan substitusi perintah di dalam(
)
sintaks pembuatan array, dan mengatur ulangIFS
setelah itu, yang lebih kompleks dari yang terlihat. Jawaban terdon mencakup beberapa pendekatan itu. Tapi saya sarankan Anda menggunakanmapfile
, perintah built-in Bash shell untuk membaca teks sebagai array baris. Ini kasus sederhana di mana Anda membaca dari sebuah file:Ini membaca baris ke dalam array yang disebut
MAPFILE
. Tanpa pengalihan input , Anda akan membaca dari input standar shell (biasanya terminal Anda) alih-alih file yang dinamai oleh . Untuk membaca ke dalam array selain dari default , berikan namanya. Sebagai contoh, ini berbunyi ke dalam array :< filename
filename
MAPFILE
lines
Perilaku default lain yang mungkin Anda pilih untuk diubah adalah bahwa karakter baris baru di ujung baris dibiarkan di tempatnya; mereka muncul sebagai karakter terakhir di setiap elemen array (kecuali input berakhir tanpa karakter baris baru, dalam hal ini elemen terakhir tidak memilikinya). Untuk mengejar baris baru ini sehingga tidak muncul dalam array, berikan
-t
opsimapfile
. Misalnya, ini membaca arrayrecords
dan tidak menulis karakter baris baru ke elemen arraynya:Anda juga dapat menggunakan
-t
tanpa melewati nama array; yaitu, ia bekerja dengan nama array implisitMAPFILE
juga.The
mapfile
shell builtin mendukung pilihan lain, dan dapat alternatif dipanggil sebagaireadarray
. Jalankanhelp mapfile
(danhelp readarray
) untuk detail.Tetapi Anda tidak ingin membaca dari file, Anda ingin membaca dari output dari perintah. Untuk mencapai itu, gunakan substitusi proses . Perintah ini membaca baris dari perintah
some-command
denganarguments...
sebagai argumen baris perintah dan menempatkannya dalammapfile
larik defaultMAPFILE
, dengan karakter baris baru yang dihapus dihapus:Substitusi proses menggantikan dengan nama file aktual dari mana output dari menjalankan dapat dibaca. File tersebut adalah pipa bernama daripada file biasa dan, di Ubuntu, itu akan dinamai seperti (kadang-kadang dengan beberapa nomor selain ), tetapi Anda tidak perlu khawatir dengan detailnya, karena shell menangani itu semua di belakang layar.
<(some-command arguments...)
some-command arguments...
/dev/fd/63
63
Anda mungkin berpikir Anda bisa melupakan proses substitusi dengan menggunakan gantinya, tetapi itu tidak akan berhasil karena, ketika Anda memiliki pipeline dari beberapa perintah yang dipisahkan oleh , Bash menjalankan menjalankan semua perintah dalam subkulit . Dengan demikian keduanya dan berjalan di lingkungan mereka sendiri , diinisialisasi dari tetapi terpisah dari lingkungan shell di mana Anda menjalankan pipa. Dalam subkulit tempat berjalan, array tidak diisi, tetapi kemudian array tersebut dibuang saat perintah berakhir. tidak pernah dibuat atau dimodifikasi untuk pemanggil.
some-command arguments... | mapfile -t
|
some-command arguments...
mapfile -t
mapfile -t
MAPFILE
MAPFILE
Inilah yang contoh di jawaban Terdon ini terlihat seperti, secara keseluruhan, jika Anda menggunakan
mapfile
:Itu dia. Anda tidak perlu memeriksa apakah
IFS
sudah disetel , melacak apakah belum disetel dan dengan nilai apa, setel ke baris baru, lalu setel ulang atau atur ulang pengaturan nanti. Anda tidak perlu menonaktifkan globbing (misalnya, denganset -f
) - yang benar-benar diperlukan jika Anda menggunakan metode yang serius, karena nama file mungkin berisi*
,?
dan[
--then mengaktifkan kembali itu (set +f
) setelah itu.Anda juga dapat mengganti loop tertentu sepenuhnya dengan satu
printf
perintah - meskipun ini sebenarnya bukan manfaatmapfile
, karena Anda dapat melakukannya apakah Anda menggunakanmapfile
atau metode lain untuk mengisi array. Ini versi yang lebih pendek:Penting untuk diingat bahwa, seperti yang disebutkan terdon , operasi baris demi baris tidak selalu sesuai, dan tidak akan berfungsi dengan benar dengan nama file yang mengandung baris baru. Saya merekomendasikan untuk tidak menamai file seperti itu, tetapi itu bisa terjadi, termasuk secara tidak sengaja.
Sebenarnya tidak ada solusi satu ukuran untuk semua.
Anda meminta "perintah umum untuk semua skenario," dan pendekatan menggunakan
mapfile
pendekatan yang agak mendekati tujuan itu, tetapi saya mendorong Anda untuk mempertimbangkan kembali kebutuhan Anda.Tugas yang ditunjukkan di atas lebih baik dicapai hanya dengan satu
find
perintah:Anda juga bisa menggunakan perintah eksternal ingin
sed
menambahkanDIR:
ke awal setiap baris. Ini bisa dibilang agak jelek, dan tidak seperti perintah find itu akan menambah "awalan" tambahan dalam nama file yang mengandung baris baru, tetapi itu bekerja terlepas dari inputnya sehingga memenuhi persyaratan Anda dan masih lebih baik untuk membaca output menjadi sebuah variabel atau array :Jika Anda perlu mendaftar dan juga melakukan tindakan pada setiap direktori yang ditemukan, seperti menjalankan
some-command
dan melewati jalur direktori sebagai argumen,find
memungkinkan Anda melakukannya juga:Sebagai contoh lain, mari kembali ke tugas umum menambahkan awalan untuk setiap baris. Misalkan saya ingin melihat output
help mapfile
tetapi nomor baris. Saya sebenarnya tidak akan menggunakanmapfile
ini, atau metode lain yang membacanya menjadi variabel shell atau shell array. Seandainyahelp mapfile | cat -n
tidak memberikan format yang saya inginkan, saya dapat menggunakanawk
:Membaca semua output dari suatu perintah ke variabel tunggal atau array kadang-kadang berguna dan sesuai, tetapi memiliki kelemahan utama. Anda tidak hanya harus berurusan dengan kompleksitas tambahan menggunakan shell Anda ketika perintah yang ada atau kombinasi dari perintah yang sudah ada mungkin sudah melakukan apa yang Anda butuhkan juga atau lebih baik, tetapi seluruh output dari perintah harus disimpan dalam memori. Terkadang Anda mungkin tahu itu bukan masalah, tetapi kadang-kadang Anda mungkin memproses file besar.
Alternatif yang biasanya dicoba - dan kadang dilakukan dengan benar - adalah membaca input baris demi baris dengan
read -r
dalam satu lingkaran. Jika Anda tidak perlu menyimpan saluran sebelumnya saat beroperasi pada saluran selanjutnya, dan Anda harus menggunakan input panjang, maka itu mungkin lebih baik daripadamapfile
. Tapi itu juga, harus dihindari dalam kasus-kasus ketika Anda bisa mengirimkannya ke perintah yang dapat melakukan pekerjaan, yang kebanyakan kasus .sumber