0

I am using Manjaro GNU/Linux. I have a directory named files; under this directory, I have around 650 subdirectories with names such as file1, file2, file3, etc.

Under each subdirectory, there are between 2 and 11 .tif images.

I want to write a command or script to automatically convert all .tif images to .jpeg or .jpg so they remain in the same subdirectory and have the same name as before.

I know that there is the command:

convert source.tif ~/converted.jpg

Can you please help?

Thanks!

Kusalananda
  • 333,661
Arun
  • 127
  • 1
    Consider https://unix.stackexchange.com/a/502963/117549 or https://unix.stackexchange.com/a/29539/117549 or https://unix.stackexchange.com/a/144102/117549 or https://unix.stackexchange.com/q/29869/117549, and probably others. – Jeff Schaller May 14 '20 at 13:04

4 Answers4

1

The simplest way of doing this would be with find executing a short in-line script that converts each found file with the .tif filename suffix:

find files/ -name '*.tif' -type f -exec sh -c '
    for pathname do
        convert "$pathname" "${pathname%.tif}.jpg"
    done' sh {} +

The in-line sh -c script above will be called by find with batches of found pathnames to regular files with the .tif suffix found under the files directory. The ${pathname%.tif}.jpg parameter substitution removes the suffix .tif from the value of $pathname and appends .jpg at the end.

Use files/file*/ as the search paths instead of files/ to look only in these ~650 subdirectories of files that start with the string file, ignoring any other subdirectory.

This leaves the .tif files in place, but you can remove them after successful conversion if you add && rm "$pathname" after the convert command in the loop.


In the zsh shell, you could do away with find and instead rely on the shell's more advanced filename globbing and string manipulation capabilities:

for pathname in files/**/*.tif(D-.); do
    convert $pathname $pathname:r.jpg
done

Here, files/**/*.tif(D-.) will expand to a list of all the regular files that have names ending in .tif anywhere in or below the files directory. The (D-.) at the end of the glob makes the globbing pattern only match regular files (possibly with "hidden" names).

The :r after $pathname gives you the "root" of the pathname (the name without the filename extension).

Use files/file*/**/*.tif(D-.) to restrict the search to only file in the file* subdirectories.


With the bash shell, you can do something similar to the above like so:

shopt -s dotglob failglob globstar
for pathname in files/**/*.tif; do
    convert "$pathname" "${pathname%.tif}.jpg"
done

The shell options set here enable the globbing of hidden names and the use of the ** globbing operator. It also makes non-matching patterns generate an error, just like by default in the zsh shell.

Kusalananda
  • 333,661
0

With 7,000+ files you'll be there a long while doing them sequentially, so I would recommend taking advantage of today's multicore CPUs and using GNU Parallel to get them converted in parallel. Not only is it faster, it can also manipulate the suffix for you very simply and give you a progress report and do a dry-run to see what it would do without actually doing anything.

In bash that would look like this:

shopt -s failglob globstar
parallel --dry-run magick {} {.}.jpg ::: files/**/*.tif

Output

magick files/file1/1.tif files/file1/1.jpg
magick files/file2/1.tif files/file2/1.jpg
magick files/file1/2.tif files/file1/2.jpg
magick files/file2/2.tif files/file2/2.jpg
magick files/file3/1.tif files/file3/1.jpg
magick files/file3/2.tif files/file3/2.jpg

If that looks correct, remove the --dry-run and do it again for real.


If you have too many files and get an error, you can pipe the filenames into magick from find, and here I also add --bar for a progress bar:

find files -iname "*.tif" -print0 | parallel -0 --dry-run --bar magick {} {.}.jpg

Note that, unlike certain answers, this will also work if your TIFF files have an uppercase suffix.

-1

After you cd into the main directory containing the subfolders.

IFS=$'\n'
for i in $(find -type f -name "*tiff"); do 
    echo $i
    convert $i $i.jpg
done

find -type f -name "*tiff" -exec rm {} \;


IFS is the internal field separator

The one problem i am seeing is that i was not able to extract the basename of the file

Dont forget to first try in a small folder before you move to big

Praveen Kumar-M
  • 592
  • 5
  • 15
-1

I would use a script similar to this one:

#!/bin/bash
if [[ $# -gt 0 ]]
then
  if [[ -d $1 ]]
  then
    cd $1
  else
    echo "Given path leads to a file or the directory you are loking does not exist!"
    exit 1
  fi
fi
for dir in */
do
  echo "Changing directory to $dir"
  cd $dir
  for image in *
  do
    if [[ -f "$image" ]]
    then
      filename="${image%.*}"
      extension="${image##*.}"
      if [[ "$extension" =~ ^ti[f]{1,2}$ ]]; then
        echo "Converting $image to ${filename}.jpg"
        convert "$image" -compress JPEG -quality 50 "${filename}.jpg"
      fi
    fi
  done
  cd - > /dev/null
done

Setting up your script:

  1. Save this as batchJPEGConvert.sh
  2. Use chmod 755 batchJPEGConvert.sh command to make this file executable
  3. Use sudo cp batchJPEGConvert.sh /usr/local/bin command to make this script accessible across your system

You can run this script by typing either batchJPEGConvert.sh or batchJPEGConvert.sh path. The first option will run this script in the current working directory. The other option will try to change the working directory to a given path and run the script there. This script will only convert files ending with .tiff or .tif extensions. It will also apply JPEG compression.

Note that this script only works for directories which structure is similar to this one:

test
├── 1
│   ├── polski.jpg
│   └── polski.tiff
└── 2
    ├── polski.jpg
    └── polski.tif