Perbarui 2020 untuk Pengguna Linux:
Jika Anda memiliki versi up-to-date dari bash (4.4-alpha atau lebih baik), karena Anda mungkin lakukan jika Anda berada di Linux, maka Anda harus menggunakan jawaban Benjamin W. ini .
Jika Anda menggunakan Mac OS, yang — terakhir saya periksa — masih menggunakan bash 3.2, atau menggunakan bash yang lebih lama, lanjutkan ke bagian berikutnya.
Jawaban untuk bash 4.3 atau sebelumnya
Berikut adalah salah satu solusi untuk mendapatkan output find
menjadi bash
array:
array=()
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done < <(find . -name "${input}" -print0)
Ini rumit karena, secara umum, nama file dapat memiliki spasi, baris baru, dan karakter bermusuhan skrip lainnya. Satu-satunya cara untuk menggunakan find
dan memisahkan nama file dengan aman satu sama lain adalah menggunakan -print0
yang mencetak nama file yang dipisahkan dengan karakter null. Ini tidak akan merepotkan jika bash readarray
/ mapfile
functions mendukung string yang dipisahkan null, tetapi tidak. Bash read
melakukannya dan itu membawa kita ke loop di atas.
[Jawaban ini aslinya ditulis pada tahun 2014. Jika Anda memiliki versi bash terbaru, lihat pembaruan di bawah.]
Bagaimana itu bekerja
Baris pertama membuat array kosong: array=()
Setiap kali read
pernyataan itu dijalankan, nama file yang dipisahkan dengan null dibaca dari input standar. The -r
pilihan memberitahu read
meninggalkan karakter backslash saja. The -d $'\0'
memberitahu read
bahwa input akan null dipisahkan. Karena kita menghilangkan nama untuk read
, shell menempatkan masukan ke nama default: REPLY
.
The array+=("$REPLY")
Pernyataan menambahkan nama file baru untuk array array
.
Baris terakhir menggabungkan pengalihan dan substitusi perintah untuk memberikan keluaran find
ke masukan standar while
perulangan.
Mengapa menggunakan substitusi proses?
Jika kami tidak menggunakan substitusi proses, loop dapat ditulis sebagai:
array=()
find . -name "${input}" -print0 >tmpfile
while IFS= read -r -d $'\0'; do
array+=("$REPLY")
done <tmpfile
rm -f tmpfile
Di atas, output dari find
disimpan dalam file sementara dan file itu digunakan sebagai input standar untuk loop sementara. Ide substitusi proses adalah membuat file-file sementara tidak diperlukan. Jadi, alih-alih while
mendapatkan loop mendapatkan stdin dari tmpfile
, kita bisa mendapatkannya dari stdin <(find . -name ${input} -print0)
.
Substitusi proses sangat berguna. Di banyak tempat di mana perintah ingin membaca dari sebuah file, Anda dapat menentukan substitusi proses <(...)
, sebagai ganti nama file. Ada bentuk analogi`` >(...)
yang dapat digunakan sebagai pengganti nama file di mana perintah ingin menulis ke file.
Seperti array, substitusi proses adalah fitur bash dan shell tingkat lanjut lainnya. Ini bukan bagian dari standar POSIX.
Alternatif: lastpipe
Jika diinginkan, lastpipe
dapat digunakan sebagai pengganti proses substitusi (ujung topi: Caesar ):
set +m
shopt -s lastpipe
array=()
find . -name "${input}" -print0 | while IFS= read -r -d $'\0'; do array+=("$REPLY"); done; declare -p array
shopt -s lastpipe
memberi tahu bash untuk menjalankan perintah terakhir di pipeline di shell saat ini (bukan latar belakang). Dengan cara ini, array
sisa - sisa yang ada setelah pipa selesai. Karena lastpipe
hanya berpengaruh jika kontrol pekerjaan dimatikan, kita jalankan set +m
. (Dalam skrip, sebagai lawan dari baris perintah, kontrol pekerjaan dinonaktifkan secara default.)
Catatan tambahan
Perintah berikut membuat variabel shell, bukan array shell:
array=`find . -name "${input}"`
Jika Anda ingin membuat sebuah array, Anda perlu meletakkan parens di sekitar output find. Jadi, secara naif, seseorang dapat:
array=(`find . -name "${input}"`)
Masalahnya adalah bahwa shell melakukan pemisahan kata pada hasil find
sehingga elemen array tidak dijamin sesuai keinginan Anda.
Perbarui 2019
Dimulai dengan versi 4.4-alpha, bash sekarang mendukung -d
opsi sehingga loop di atas tidak lagi diperlukan. Sebaliknya, seseorang dapat menggunakan:
mapfile -d $'\0' array < <(find . -name "${input}" -print0)
Untuk informasi lebih lanjut tentang ini, silakan lihat (dan upvote) jawabannya Benjamin W. ini .
IFS=
menghindari penghapusan spasi putih dari awal atau akhir baris masukan. Anda dapat mengujinya dengan mudah dengan membandingkan keluaran dariread var <<<' abc '; echo ">$var<"
dengan keluaran dariIFS= read var <<<' abc '; echo ">$var<"
. Dalam kasus sebelumnya, spasi sebelum dan sesudahabc
dihilangkan. Yang terakhir, mereka tidak. Nama file yang dimulai atau diakhiri dengan spasi mungkin tidak biasa tetapi, jika ada, kami ingin agar diproses dengan benar.<'
selesai <<(temukan aaa / -not -newermt "$ last_build_timestamp_v" -type f -print0) '''
dapat digunakan sebagai pengganti$'\0'
:n=0; while IFS= read -r -d '' line || [ "$line" ]; do echo "$((++n)):$line"; done < <(printf 'first\nstill first\0second\0third')
BLAH=$(find . -name '*.php')
. Seperti yang dibahas dalam jawaban, pendekatan itu akan bekerja dalam kasus terbatas tetapi tidak akan berfungsi secara umum dengan semua nama file dan tidak menghasilkan, seperti yang diharapkan OP, sebuah array .Bash 4.4 memperkenalkan
-d
opsi kereadarray
/mapfile
, jadi ini sekarang dapat diselesaikan denganreadarray -d '' array < <(find . -name "$input" -print0)
untuk metode yang bekerja dengan nama file sembarang termasuk kosong, baris baru, dan karakter globbing. Ini membutuhkan
find
dukungan Anda-print0
, seperti misalnya GNU find.Dari manual (menghilangkan opsi lain):
Dan
readarray
itu hanya sinonim darimapfile
.sumber
Jika Anda menggunakan
bash
4 atau lebih baru, Anda dapat mengganti penggunaanfind
denganshopt -s globstar nullglob array=( **/*"$input"* )
The
**
Pola diaktifkan olehglobstar
pertandingan 0 atau lebih direktori, yang memungkinkan pola untuk mencocokkan dengan kedalaman sewenang-wenang dalam direktori saat ini. Tanpanullglob
opsi, pola (setelah perluasan parameter) diperlakukan secara harfiah, jadi tanpa kecocokan Anda akan memiliki larik dengan string tunggal, bukan larik kosong.Tambahkan
dotglob
opsi ke baris pertama juga jika Anda ingin melintasi direktori tersembunyi (suka.ssh
) dan juga mencocokkan file tersembunyi (suka.bashrc
).sumber
nullglob
juga…dotglob
sudah diatur (ini mungkin atau mungkin tidak diinginkan, tetapi perlu disebutkan juga).Anda bisa mencoba sesuatu seperti
, dan untuk mencetak nilai array, Anda dapat mencoba sesuatu seperti echoarray=(`find . -type f | sort -r | head -2`)
"${array[*]}"
sumber
Berikut ini tampaknya berfungsi untuk Bash dan Z Shell di macOS.
#! /bin/sh IFS=$'\n' paths=($(find . -name "foo")) unset IFS printf "%s\n" "${paths[@]}"
sumber
printf "%b" "file name with spaces, a star * ...\012and a second line\0" | xargs -0 touch
Dalam bash,
$(<any_shell_cmd>)
membantu menjalankan perintah dan menangkap hasilnya. Meneruskan ini keIFS
dengan\n
sebagai pembatas membantu mengonversinya menjadi array.IFS='\n' read -r -a txt_files <<< $(find /path/to/dir -name "*.txt")
sumber
find
ke dalam array.Anda bisa melakukan seperti ini:
#!/bin/bash echo "input : " read input echo "searching file with this pattern '${input}' under present directory" array=(`find . -name '*'${input}'*'`) for i in "${array[@]}" do : echo $i done
sumber
Bagi saya, ini berfungsi dengan baik di cygwin:
declare -a names=$(echo "("; find <path> <other options> -printf '"%p" '; echo ")") for nm in "${names[@]}" do echo "$nm" done
Ini berfungsi dengan spasi, tetapi tidak dengan tanda kutip ganda (") di nama direktori (yang toh tidak diizinkan di lingkungan Windows).
Hati-hati dengan ruang di opsi -printf.
sumber