2

I am running Ubuntu 18.04. I have a directory full of storyboard images in .jpg format as below.

image-0000.jpg
image-0001.jpg
image-0002.jpg
image-0003.jpg
image-0004.jpg
.
.
image-3898.jpg
image-3899.jpg

Merging 13 images vertically gives me a Single page. So I think I need to use below command, using a range of 13 numbers at a time in a loop and save to a directory "./Merged".

convert -append image-{range of 13}.jpg ./Merged/page_001.jpg

My experiment and thought process is as below.

I am trying to use a nested for loop and seq -w as below. But I am unable understand, how to loop the scrip in such a way that it takes first 13 files (from image-0000 to image-0012), merges them and saves in the ./Merged/ folder. Then come out of the loop and again take the next 13 files (from image-0013 to image-0025) and so on. Till all .jpg files in the current folder are finished or till 300 pages are generated.

My Script

#!/bin/bash

As 3899 image slices will be converted to 300 pages

I thought to run for loop 300 times

for ((page=1; page<=300; page++)) do

As images are slices of pages.

for slices in $(seq -w 0 3899) do

# We need to merge 13 times so...
# Should i use for loop with increment as below?
# for ((smerge=1; smerge&lt;=13; smerge++))
# do
#   convert &quot;SOME LOGIC&quot; ./Merged/page_001.jpg
# done

# **OR**
# somehow take 13 numbers from sequence

    convert image-$slices_{RANGE}.jpg -append ./Merged/page_$page.jpg 

done

done

4 Answers4

1

So let's say you have these files in a continuous range from image-0000.jpg to image-2999.jpg. This should demonstrate the principle at work here:

#!/bin/bash
for page in {000..001}; do
  echo image-${page}{0..9}.jpg
done

So, do drop this into your use-case:

#!/bin/bash
for page in {000..001}; do
  convert image-${page}{0..9}.jpg --append ./Merged/page-${page}.jpg
done

Note that {01..02} expanding to 01 02 rather than 1 2 requires bash 4.

DopeGhoti
  • 76,081
  • Hi DopeGhoti, Thank you very much. Your solution works with 10 perfectly. But While posting I made a mistake. I need 13 increments. I have corrected my question too. Thanks for your time. – user3025253 Jun 12 '18 at 23:14
1

With zsh:

#! /bin/zsh -

typeset -Z3 page
files=(image-<0-3900>.jpg)
for ((page = 1; $#files; page++)) {
  convert $files[1,13] -append ./Merged/page_$page.jpg
  files[1,13]=()
}

Note that since there are 3901 images (13 × 300 + 1), the last page will have only one image.

You can do something similar with bash like:

#! /bin/bash -
shopt -s extglob
shopt -s failglob
set -- image-+([[:digit:]]).jpg
for ((page = 1; $#; page++)) {
  printf -v padded_page %03d "$page"
  convert "${@:1:13}" -append "./Merged/page_$padded_page.jpg"
  (($# > 13)) || break
  shift 13
}

POSIXly, assuming there are matching files and doing a yet less close check on file names:

#! /bin/sh -
set -- image-*.jpg

# disable split+glob, only retain empty removal on unquoted expansions:
set -o noglob; IFS=

page=1; while [ "$#" -gt 0 ]; do
  padded_page=000$page
  padded_page=${padded_page#"${padded_page%???}"}
  convert $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} \
    -append "./Merged/page_$padded_page.jpg"
  [ "$#" -gt 13 ] || break
  shift 13
  page=$((page + 1))
done

Note that while here the file names are very tamed (no blanks, special characters...), special care has been taken in those codes to handle arbitrary characters. However note that convert and other imagemagick utilities could have problems with file names starting with - (even when using --) or containing :, so best is to prefix file paths with ./ to avoid those problems (for instance, use ./*.jpg instead of *.jpg).

  • Thank you Stéphane Chazelas. I was able to solve yesterday morning. Still I would like to check your solution too. Soon I will post my solution too. – user3025253 Jun 14 '18 at 06:28
  • As the file naming starts from 0000 it ends at 3899, Sorry about that i Corrected it. – user3025253 Jun 14 '18 at 06:36
  • is there anything after IFS= – user3025253 Jun 20 '18 at 14:54
  • @user3025253, no. That's the point. We empty $IFS (the internal field separator), to disable word splitting. – Stéphane Chazelas Jun 20 '18 at 14:59
  • zsh script failed with errors POSIX script is creating only one file "page_001.jpg". Script is working but it is overwriting on to one file. – user3025253 Jun 20 '18 at 15:42
  • @user3025253, what errors? – Stéphane Chazelas Jun 20 '18 at 15:53
  • Your 2nd code for bash worked Perfectly

    time bash bashChazelas.sh (2 bash)

    real 0m7.300s user 0m7.163s sys 0m1.524s

    – user3025253 Jun 20 '18 at 15:58
  • @user3025253, about the POSIX one, sorry I had forgotten to increment $page. Should be fixed now. – Stéphane Chazelas Jun 20 '18 at 16:00
  • This was the error. I copied your script exactly as I dont know zsh. --->>
    line 3: typeset: -Z: invalid option line 4: syntax error near unexpected token '<' line 4: files=(image-<0-3899>.jpg) line 5: ((: 0files: value too great for base (error token is "0files")
    – user3025253 Jun 20 '18 at 16:05
  • Sorry I was Idiot. I used bash to execute the script. i will check zsh again. – user3025253 Jun 20 '18 at 16:25
  • @user3025253, those are bash error messages, you need to run that script with zsh, not bash. zsh ./thescript or ./the-script with that #! /bin/zsh - shebang and assuming zsh is available at that path and you have made the script executable (like with chmod +x the-script). – Stéphane Chazelas Jun 20 '18 at 16:26
0

You can do this using xargs -n argument and push the result to a while read loop.

page=1
while IFS= read -r row; do
    convert $row -append ./Merged/page_$page.jpg
    page=$((page + 1))
done < <(echo image-{0000..3900}.jpg | xargs -n13)
pLumo
  • 22,565
  • Thank you RoVo. I was able to solve yesterday morning. Still I would like to check your solution too. Soon I will post my solution too. – user3025253 Jun 14 '18 at 06:34
  • what is this IFS variable? any special system variable? IFS Means? – user3025253 Jun 16 '18 at 14:14
  • This means internal field separator and defines how word splitting is done. In this case not at all because I unset it. See https://unix.stackexchange.com/a/184867 – pLumo Jun 17 '18 at 06:49
  • Your Script worked perfectly and took less time than my solution. almost half the time. real 0m7.284s – user3025253 Jun 20 '18 at 16:09
  • But only problem was that I need to keep changing the range, {0000..3899} , for different situations. – user3025253 Jun 20 '18 at 16:14
0

Hello everyone. Thank you very much for your time and replies.

DopeGhoti, Philip Kendall, Stéphane Chazelas, RoVo

This is What I was able to create, and it worked. Still I would like to check others Solution too. Maybe your solution is faster than my basic Script.

NOTE: This Script will/might fail if there are spaces in file names!

only script

#!/bin/bash

NOTE: This Script will/might fail if there are spaces in file names!

mkdir -p Finished; mkdir -p Merged;

for paGe in $(seq -w 1 300) do ls *.jpg | head -n 13 > filesData.txt; paGeData=$(sed ':a;N;$!ba;s/\n/ /g' filesData.txt); convert $paGeData -append ./Merged/Page_$paGe.jpg;

while read -r line do mv $line ./Finished/ done < filesData.txt done

Script with comments

#!/bin/bash

NOTE: This Script will/might fail if there are spaces in file names!

I random capitalize letters in a variable to make them unique,

As i can't be sure.

mkdir -p Finished; mkdir -p Merged;

for paGe in $(seq -w 1 300) do

Selecting only 13 files from the current directory

ls *.jpg | head -n 13 > filesData.txt;

Replace newline (\n) with space and place "file names"

in one line and storing "sed" output to a variable.

So that we use it with "convert" command.

paGeData=$(sed ':a;N;$!ba;s/\n/ /g' filesData.txt);

Merge different images to one long image

convert $paGeData -append ./Merged/Page_$paGe.jpg;

Move files to different directory,

So that we can work with next range/batch of files.

while read -r line do mv $line ./Finished/ done < filesData.txt

done

A long thought process. If someone might be interested in how I tried to improvise the code. This might have some mistakes.

#!/bin/bash

mkdir -p Finished; mkdir -p Merged;

for paGe in $(seq -w 1 300) do

Selecting only 13 files from the current directory

ls *.jpg | head -n 13 > filesData.txt;

Replace newline (\n) with space and

place file names in one line for the convert command in while loop

sed ':a;N;$!ba;s/\n/ /g' filesData.txt > paGeData.txt;

We can remove below loop by storing sed (above) output to a variable

and substituting it later in the command.

paGeData=$(sed ':a;N;$!ba;s/\n/ /g' filesData.txt);

Merge different images to one long image

"line" is a system variable

while read -r line

do

convert $line -append ./Merged/Page_$paGe.jpg

done < paGeData.txt

convert $paGeData -append ./Merged/Page_$paGe.jpg;

Move files to different directory,

So that we can work with next range/batch of files.

while read -r line do mv $line ./Finished/ done < filesData.txt

done