Apakah ada yang punya skrip shell template untuk melakukan sesuatu dengan ls
daftar nama direktori dan perulangan masing-masing dan melakukan sesuatu?
Saya berencana melakukan ls -1d */
untuk mendapatkan daftar nama direktori.
Apakah ada yang punya skrip shell template untuk melakukan sesuatu dengan ls
daftar nama direktori dan perulangan masing-masing dan melakukan sesuatu?
Saya berencana melakukan ls -1d */
untuk mendapatkan daftar nama direktori.
Jawaban:
Diedit untuk tidak menggunakan ls di mana gumpalan akan melakukan, seperti yang disarankan @ shawn-j-goff dan lainnya.
Cukup gunakan satu for..do..done
lingkaran:
for f in *; do
echo "File -> $f"
done
Anda dapat mengganti *
dengan *.txt
atau glob lain yang mengembalikan daftar (file, direktori, atau apa pun dalam hal ini), perintah yang menghasilkan daftar, misalnya $(cat filelist.txt)
, atau benar-benar menggantinya dengan daftar.
Di dalam do
loop, Anda cukup merujuk ke variabel loop dengan awalan tanda dolar (jadi $f
dalam contoh di atas). Anda dapat echo
melakukannya atau melakukan hal lain yang Anda inginkan.
Misalnya, untuk mengganti nama semua .xml
file di direktori saat ini menjadi .txt
:
for x in *.xml; do
t=$(echo $x | sed 's/\.xml$/.txt/');
mv $x $t && echo "moved $x -> $t"
done
Atau bahkan lebih baik, jika Anda menggunakan Bash Anda dapat menggunakan ekspansi parameter Bash daripada memunculkan subkulit:
for x in *.xml; do
t=${x%.xml}.txt
mv $x $t && echo "moved $x -> $t"
done
for
loop yang sangat umum , dan perangkap khas mencoba mengurai output ls
. @ DanielA.White, Anda mungkin mempertimbangkan untuk tidak menerima jawaban ini jika itu tidak membantu (atau berpotensi menyesatkan), karena seperti yang Anda katakan, Anda bertindak berdasarkan direktori. Jawaban Shawn J. Goff harus memberikan solusi yang lebih kuat dan berfungsi untuk masalah Anda.
ls
output , Anda tidak harus membaca output dalam satu for
lingkaran , Anda harus menggunakan $()
alih-alih `` dan Use More Quotes ™ . Saya yakin @CoverosGene bermaksud baik, tetapi ini hanya mengerikan.
ls
adalah ls -1 | while read line; do stuff; done
. Setidaknya itu tidak akan rusak untuk ruang putih.
mv -v
Anda tidak perluecho "moved $x -> $t"
Menggunakan output ls
untuk mendapatkan nama file adalah ide yang buruk . Ini dapat menyebabkan skrip yang tidak berfungsi dan bahkan berbahaya. Ini karena nama file dapat berisi karakter apa pun kecuali /
dan null
karakternya, dan ls
tidak menggunakan salah satu dari karakter tersebut sebagai pembatas, jadi jika nama file memiliki spasi atau baris baru, Anda akan mendapatkan hasil yang tidak terduga.
Ada dua cara yang sangat baik untuk mengulang file. Di sini, saya hanya menggunakan echo
untuk menunjukkan melakukan sesuatu dengan nama file; Anda bisa menggunakan apa saja.
Yang pertama adalah menggunakan fitur globbing asli shell.
for dir in */; do
echo "$dir"
done
Shell mengembang */
menjadi argumen terpisah yang for
dibaca loop; bahkan jika ada spasi, baris baru, atau karakter aneh lainnya dalam nama file, for
akan melihat setiap nama lengkap sebagai unit atom; itu tidak menguraikan daftar dengan cara apa pun.
Jika Anda ingin pergi secara rekursif ke subdirektori, maka ini tidak akan melakukan kecuali shell Anda memiliki beberapa fitur globbing diperpanjang (seperti bash
's globstar
. Jika shell Anda tidak memiliki fitur ini, atau jika Anda ingin memastikan bahwa script Anda akan bekerja pada berbagai sistem, maka opsi selanjutnya adalah menggunakan find
.
find . -type d -exec echo '{}' \;
Di sini, find
perintah akan memanggil echo
dan memberikan argumen nama file. Ini melakukan ini sekali untuk setiap file yang ditemukannya. Seperti contoh sebelumnya, tidak ada penguraian daftar nama file; sebaliknya, nama file dikirim sepenuhnya sebagai argumen.
Sintaks -exec
argumen terlihat sedikit lucu. find
mengambil argumen pertama setelah -exec
dan menganggap itu sebagai program untuk dijalankan, dan setiap argumen berikutnya, dibutuhkan sebagai argumen untuk lolos ke program itu. Ada dua argumen khusus yang -exec
perlu dilihat. Yang pertama adalah {}
; argumen ini diganti dengan nama file yang find
dihasilkan oleh bagian sebelumnya . Yang kedua adalah ;
, yang memberi find
tahu ini adalah akhir dari daftar argumen untuk diteruskan ke program; find
membutuhkan ini karena Anda dapat melanjutkan dengan lebih banyak argumen yang ditujukan untuk find
dan tidak ditujukan untuk program eksekutif. Alasan untuk itu \
adalah bahwa shell juga memperlakukan;
khususnya - ia mewakili akhir dari sebuah perintah, jadi kita perlu menghindarinya sehingga shell memberikannya sebagai argumen untuk find
tidak menggunakannya untuk dirinya sendiri; Cara lain untuk mendapatkan shell untuk tidak memperlakukannya secara khusus adalah memasukkannya ke dalam tanda kutip: ';'
berfungsi sama baiknya \;
untuk tujuan ini.
find
. Ada keajaiban yang bisa dilakukan, dan bahkan keterbatasan yang dirasakan -exec
dapat diselesaikan. The -print0
pilihan juga berharga untuk digunakan dengan xargs
.
Untuk file dengan spasi di Anda harus memastikan untuk mengutip variabel seperti:
for i in $(ls); do echo "$i"; done;
atau, Anda dapat mengubah variabel lingkungan pemisah bidang input (IFS):
IFS=$'\n';for file in $(ls); do echo $i; done
Akhirnya, tergantung pada apa yang Anda lakukan, Anda bahkan mungkin tidak memerlukan ls:
for i in *; do echo "$i"; done;
\n
tidak cukup. Tidak pernah merupakan ide yang bagus untuk merekomendasikan parsing output dari ls
.
Jika Anda telah menginstal GNU Parallel http://www.gnu.org/software/parallel/ Anda dapat melakukan ini:
ls | parallel echo {} is in this dir
Untuk mengganti nama semua .txt menjadi .xml:
ls *.txt | parallel mv {} {.}.xml
Tonton video intro untuk GNU Parallel untuk mempelajari lebih lanjut: http://www.youtube.com/watch?v=OpaiGYxkSuQ
Mengapa tidak mengatur IFS ke baris baru, lalu menangkap output ls
dalam array? Pengaturan IFS ke baris baru harus menyelesaikan masalah dengan karakter lucu dalam nama file; menggunakan ls
bisa menyenangkan karena memiliki fasilitas sortir bawaan.
(Dalam pengujian saya mengalami kesulitan pengaturan IFS untuk \n
tetapi pengaturannya untuk backspace karya baris baru, seperti yang disarankan di tempat lain di sini):
Misalnya (dengan asumsi ls
pola pencarian yang diinginkan diteruskan $1
):
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
FILES=($(/bin/ls "$1"))
for AFILE in ${FILES[@]}
do
... do something with a file ...
done
IFS=$SAVEIFS
Ini sangat berguna pada OS X, misalnya, untuk mengambil daftar file yang diurutkan berdasarkan tanggal pembuatan (terlama ke terbaru), ls
perintahnya adalah ls -t -U -r
.
\n
tidak cukup. Satu-satunya solusi yang valid menggunakan loop for dengan ekspansi pathname, atau find
.
Ini adalah bagaimana saya melakukannya, tetapi mungkin ada cara yang lebih efisien.
ls > filelist.txt
while read filename; do echo filename: "$filename"; done < filelist.txt
ls | while read i; do echo filename: $i; done