it-swarm.asia

mengulang melalui `ls` menghasilkan skrip bash Shell

Apakah ada yang punya skrip template Shell untuk melakukan sesuatu dengan ls untuk daftar nama direktori dan mengulanginya masing-masing dan melakukan sesuatu?

Saya berencana melakukan ls -1d */ untuk mendapatkan daftar nama direktori.

88
Daniel A. White

Diedit untuk tidak menggunakan ls di mana glob akan melakukan, seperti yang disarankan @ shawn-j-goff dan lainnya.

Cukup gunakan loop for..do..done:

for f in *; do
  echo "File -> $f"
done

Anda dapat mengganti * dengan *.txt atau glob lainnya 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.

Dalam loop do, Anda cukup merujuk ke variabel loop dengan awalan tanda dolar (jadi $f pada contoh di atas). Anda dapat echo atau melakukan hal lain yang Anda inginkan.

Misalnya, untuk mengganti nama semua file .xml dalam 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
101
CoverosGene

Menggunakan output lsuntuk 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 nullname__character, dan lstidak menggunakan salah satu karakter tersebut sebagai pembatas, jadi jika nama file memiliki spasi atau baris baru, Anda akan dapatkan hasil yang tidak terduga.

Ada dua cara yang sangat baik untuk mengulang file. Di sini, saya hanya menggunakan echountuk 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 memperluas */ menjadi argumen terpisah yang dibaca loop forname__; bahkan jika ada spasi, baris baru, atau karakter aneh lainnya dalam nama file, forakan melihat setiap nama lengkap sebagai unit atom; itu tidak menguraikan daftar dengan cara apa pun.

Jika Anda ingin masuk ke subdirektori secara rekursif, maka ini tidak akan berfungsi kecuali jika Shell Anda memiliki beberapa fitur globbing yang diperluas (seperti bashname __'s globstarname__. Jika Shell Anda tidak memiliki fitur ini, atau jika Anda ingin memastikan bahwa skrip Anda akan berfungsi pada berbagai sistem, maka opsi selanjutnya adalah menggunakan findname__.

find . -type d -exec echo '{}' \;

Di sini, perintah findakan memanggil echodan 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 argumen -exec terlihat sedikit lucu. findmengambil argumen pertama setelah -exec dan memperlakukannya sebagai program yang akan dijalankan, dan setiap argumen berikutnya, diperlukan argumen untuk meneruskan ke program itu. Ada dua argumen khusus yang perlu dilihat -exec. Yang pertama adalah {}; argumen ini diganti dengan nama file yang dihasilkan oleh bagian-bagian sebelumnya dari findname__. Yang kedua adalah ;, yang memungkinkan findtahu bahwa ini adalah akhir dari daftar argumen untuk diteruskan ke program; findmembutuhkan ini karena Anda dapat melanjutkan dengan lebih banyak argumen yang ditujukan untuk finddan tidak ditujukan untuk program eksekutif. Alasan untuk \ adalah bahwa Shell juga memperlakukan ; secara khusus - ia mewakili akhir dari sebuah perintah, jadi kita perlu menghindarinya sehingga Shell memberikannya sebagai argumen untuk finddaripada mengkonsumsinya sendiri; Cara lain untuk membuat Shell tidak memperlakukannya secara khusus adalah memasukkannya ke dalam tanda kutip: ';' berfungsi sama baiknya dengan \; untuk tujuan ini.

64
Shawn J. Goff

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;
14
john

Jika Anda memiliki GNU Paralel http://www.gnu.org/software/parallel/ diinstal, 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 Paralel untuk mempelajari lebih lanjut: http://www.youtube.com/watch?v=OpaiGYxkSuQ

5
Ole Tange

Hanya untuk menambahkan jawaban CoverosGene, berikut adalah cara untuk mendaftar hanya nama direktori:

for f in */; do
  echo "Directory -> $f"
done
4
n3o

Mengapa tidak mengatur IFS ke baris baru, lalu menangkap output dari ls dalam array? Pengaturan IFS ke baris baru harus menyelesaikan masalah dengan karakter lucu dalam nama file; menggunakan ls dapat menjadi Nice karena memiliki fasilitas sortir bawaan.

(Dalam pengujian saya mengalami kesulitan mengatur IFS ke \n tetapi pengaturannya untuk backspace baris baru berfungsi, seperti yang disarankan di tempat lain di sini):

Misalnya. (dengan asumsi pola pencarian ls yang diinginkan diteruskan dalam $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, mis., Untuk menangkap daftar file yang diurutkan berdasarkan tanggal pembuatan (terlama ke terbaru), perintah ls adalah ls -t -U -r.

1
jetset