Iterate atas n file?


8

Saya punya sesuatu yang cukup sederhana yang ingin saya lakukan. Saya ingin menggunakan montagepada direktori yang berisi ribuan gambar, dengan sedikit pilihan, yaitu:

me@home$ montage -size 256x256 DSC01*.JPG.svg output.png

... tapi itu tidak cukup baik, karena hanya mengambil sekitar 100 gambar sekaligus; tidak juga

me@home$ montage -size 256x256 *.svg output.png

... yang mengambil semua gambar secara bersamaan, karena file yang dihasilkan terlalu besar untuk diurai.

Yang ingin saya lakukan adalah mengulangi lebih dari sekitar 100-200 file sekaligus. Saya kira ini bisa diimplementasikan dengan menggunakan for loop (?), Tapi saya hanya sedikit bingung bagaimana cara melakukannya. Saya kira mungkin ada cara cerdas untuk menggunakannya find -execatau xargsyang tidak saya pikirkan. Saya menggunakan bash, tetapi saya menggunakan zshsesekali.

Jadi, sebagai kesimpulan, saya mencari satu liner yang, diberikan 2600 file gambar, panggilan montase sekitar 13 atau 26 kali (satu kali untuk setiap 100-200 file), dan diberikan n file, dapat disebut kelipatan n kali .


1
Apakah semua file Anda bernama DSC0100.JPG.svg... DSC2600.JPG.svg?
jw013

Jawaban:


6

Sebuah bashmetode, menggunakan array yang khusus fitur; mungkin diterjemahkan zshdengan beberapa modifikasi:

image_files=(*.svg) # use your own glob expression
n=200               # number of files per command line; adjust to taste
for ((i=0; i < ${#image_files[@]}; i+=n)); do
        montage -size 256x256 "${image_files[@]:i:n}" output-"$i".png
done

1
Saya menemukan sedikit scripting bash ini juga sangat extensible. Saya hanya menggunakannya untuk memindahkan beberapa file (16 file per direktori) dan berhasil pada percobaan pertama, yang sedikit mengejutkan. Terima kasih.
ixtmixilix

5

Anda dapat menggunakan xargs untuk itu; sayangnya, tidak mungkin untuk menggabungkan -I (untuk menanamkan ke tengah-tengah baris perintah) dan -L (untuk membatasi jumlah file untuk satu panggilan ke executable). Oleh karena itu, saya membuat baris perintah ini sebagai contoh (tapi waspadalah terhadap karakter khusus dalam nama file, mereka tidak didukung):

 ls . | \
   xargs -n 100 echo | \
   (a=1; 
    while read args; do 
     echo montage -size 256x256 $args output-$a.png;
     a=$((a+1)); 
    done
   )

Hapus echojika Anda ingin benar-benar menjalankan perintah.

Peringatan:

  • nama file tidak boleh mengandung spasi atau karakter khusus lainnya
  • baris montase terakhir mungkin memiliki kurang dari 100 file di dalamnya

Memperbarui:

Ini adalah yang sesuai untuk loop, yang (saya harap) menyelesaikan masalah dengan spasi dalam nama file:

a=0
b=0
lst=
for f in *; do 
  a=$((a+1))
  lst="$lst '$f'"
  if test $a -ge 100; then 
    eval echo montage --args $lst target-$b.png
    b=$((b+1))
    a=0
    lst=
  fi 
done

Pembaruan 2: Solusi python, yang seharusnya kebal terhadap karakter khusus dalam nama file

#!/usr/bin/env python
# iterate.py

"""Usage: 
%prog <number per call> <file pattern> <command prefix> -- <command postfix>
e.g.  %prog 100 "DSC01*.jpg.svg" montage -size 256x256 -- output-%i.png """

import sys,subprocess,glob,os

if len(sys.argv) < 5: 
  print __doc__.replace("%prog", os.path.basename(sys.argv[0]))
  sys.exit(1)

def chunks(l, n): 
  for i in xrange(0, len(l), n): yield l[i:i+n]

num, pattern, args = int(sys.argv[1]), sys.argv[2], sys.argv[3:]
files, idx = glob.glob(pattern), args.index("--")
before, after = args[0:idx], args[idx+1:]

for idx,chunk in enumerate(chunks(files,num)):
  subprocess.call( before + chunk + [s.replace("%i",str(idx)) for s in after] )

2
Jika Anda akan merekomendasikan menggunakan lspipa untuk mengurai hasilnya, Anda juga harus memperingatkan tentang banyak bahaya melakukan hal itu dengan jelas, dan pada awalnya untuk memastikan orang melihatnya.
jw013

@ jw013 +1 Ya, itu pasti masalah. Namun, postingannya saya anggap dia menggunakan foto yang diimpor langsung dari kamera digital, yang tidak mengandung karakter khusus. Bagaimana Anda menyarankan untuk mengatasi masalah itu?
daniel kullmann

Ya, sepertinya nama file relatif jinak (karenanya tidak ada downvote). Namun OP belum benar-benar menentukan seperti apa kelihatannya *.svg(itulah sebabnya saya mengirim komentar pada pertanyaan yang diajukan). Dalam kasus yang paling umum di mana Anda perlu menangani semua nama file, Anda harus menggunakan shell globbing dan array atau find -print0 | xargs -0konstruksi. Lihat jawaban saya untuk contoh yang pertama.
jw013

@ jw013 Jawaban Anda sangat bagus! Saya tidak pernah berusaha mempelajari bagaimana array bekerja di bash. Mungkin aku sebaiknya.
daniel kullmann

2

Ini adalah versi menggunakan xargs yang aman untuk nama file apa pun, tetapi memerlukan file sementara untuk menyimpan hitungan. Sesuaikan '-n 100' untuk menyesuaikan berapa banyak file per montase. Anda juga dapat menukar "printf" dengan "find -print0", tetapi pastikan tidak menemukan "count.temp".

echo 1 >count.temp
printf "%s\0" *.svg | xargs -0 -n 100 sh -c '
    a=`cat count.temp`
    montage --blah "$@" output-"$a".png
    let a=a+1
    echo "$a" >count.temp
    '
rm count.temp

2

Dengan GNU Parallel Anda dapat melakukan:

parallel -N200 montage -size 256x256 {} output{#}.png ::: *.svg

Tentu saja aman untuk file dengan karakter khusus (seperti yang biasanya Anda harapkan dari GNU Parallel).

Instalasi minimal

Jika Anda hanya perlu paralel dan tidak menginstal 'make' (mungkin sistemnya sudah lama atau Microsoft Windows):

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem
mv parallel sem dir-in-your-$PATH/bin/

Tonton video intro untuk perkenalan cepat: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1 atau di http://tinyogg.com/watch/TORaR/ dan http://tinyogg.com/watch/hfxKj /

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.