how to find files and execute program on them - linux

I was trying to run a program in Linux with syntax like this:
BET2 <input file> <output file>
This program would take an image and perform some preprocessing on it then save to a new file. Now I could run the program correctly. However, I have about 1 million images and I don't want to run them one by one manually.
So, my question is, is there any way I could do the following:
find all the images (.jpg file) under the current directory
let each image (something.jpg) be the input of the preprocessing program and name the output with something_processed.jpg

You could use something like
ls *.jpg | xargs -I{} BET2 {} processed_{}

You can achieve your task by executing a command similar to the one below:
cd "/path/to/the/folder/containing/your/images"
for CURRENT_IMAGE in `find *.jpg -maxdepth 0`; do
echo "Preprocessing image file $CURRENT_IMAGE"
# Your program that performs the image preprocessing
BET2 "$CURRENT_IMAGE" "${CURRENT_IMAGE}_processed.jpg"
done
Hope it helps :)

Related

bash - opening an image only when a corresponding text file exists

I came across a problem in Bash when I would try to only open images based upon the information stored in .txt files about them. I am trying to sort a number of images by size or height, and display an image with them in the sorted order, but if there exists a .jpg in the folder without a .txt file with the same name, it should not process it.
I have the sorting piece of my situation done, and am trying to figure out how I would go about opening only the images that have a .jpg extension WITH a .txt file.
I figured a solution would look like me putting every .jpg's name (without extension) in a list and then process through the list and run something like:
[if -f $filename.txt ]; then ~~~
but I came across the problem of iterating through without a for-loop, or else all the pictures would open multiple times. My attempt was:
for i in *jpg; do
y=$y ${i.jpg}
done
if[ -f $y.txt ] then
(sorting parts)
This only looked at the last filename in y, as it should, but I am trying to figure out a way to look at each separate filename and see if there exists that textfile, in order to include it in the sorting.
Thanks so much for your help!
Collecting a list of file names in a single variable is an antipattern. You want to collect them in an array instead.
a=()
for f in *.jpg; do
if [ -e "${f%.jpg}".txt ]; then
continue
fi
a+=("$f")
done
# now do things with "${a[#]}"
Frequently, you don't really need to collect the files in an array -- just do everything you were doing inside the for loop to each individual file as you traverse the files.
(And actually y=$y ${i%.jpg} doesn't append to y -- it sets y to itself for the duration of attempting to execute a file named i sans the .jpg extension, which would most likely fail in the vast majority of cases.)
I would do the file check first such that find just reports files that have a corresponding text file. The following snippet will just display jpg files that have a corresponding txt file:
find . -name "*.jpg" -maxdepth 1 -exec /bin/bash -c '[ -e "${0%.*}.txt" ] && echo "$0";' {} \;

BASH Linux Run code for all file extensions ".fal"

I am not a Linux user, so bash and shell are new for me.
I need a code that runs 2 scripts for all file extensions ".fal" that are located in the folder(and sub-folders preferably) that I run the code in.
E.g:
dos2unixfortxtandfal """""""that code runs for all files in the folder already
and
for all ".fal" files in this folder,
Do
eine_fal_macher (here the .fal files 1 by one) Versuch.txt
Done
eine_fal_marcher --> this is the script that runs in the moment only once
(here the .fal files 1 by one) --> this is input file 1
Versuch.txt--> this is input file 2 (same for all) (from the same
folder)
In the end I want to do the following in the terminal:
frdc09927:\Frdc09927\z183464\DOE_Wellen\21a>
frdc09927:\Frdc09927\z183464\DOE_Wellen\21a>script.bash --> Enter
frdc09927:\Frdc09927\z183464\DOE_Wellen\21b>script.bash --> Enter
frdc09927:\Frdc09927\z183464\DOE_Wellen\21c>script.bash --> Enter
find . -name \*.fal -exec eine_fal_macher {} Versuch.txt \;
This runs for all *.fal files in the current directory and its subdirectories. Use -maxdepth 1 as first option to limit it to the current directory only, or give a different working directory than . to have find search somewhere else. {} is replaced with the "found" filename, honoring things like spaces in the filename automatically.
I could start explaining find at this point, but you should really rather have a look at man find instead. This tool is extremely powerful, and can reduce rather complex problems (like acting on the age of files, their owners etc.) to a one-liner.
Try something like this:
for i in `ls *.fal`; do command1 $i && command2 $i; done
command2 is only executed for a specific file if command1 does not return an errorcode
I'm not sure I fully understand the requirement, but here goes (trying to follow your pseudo code):
for FILE in `find . -name "*.fal"`
do
eine_fal_macher "${FILE}" Versuch.txt
done

High-throughput viewing and selecting of photos

From of the thousands and thousands of personal photographs in my collection, I'd like to select some special ones to print and display as a collage. All the photos are on one hard drive but scattered around /home/$USER. I know how to find all jpg photos with a command like this: find / -iname "*.jpg" -print. But that only lists the file name. I could run a similar command to view the file, but that is only half the challenge.
How can I then view each photograph and also have a dialog for whether or not to copy the photo to the directory that will be printed? (For example, with fdupes -r -d /home/$USER I can see a dialog about which file to delete.
(Some background, I used ubuntu 12.04 x64 and I'm comfortable with the terminal.)
# assuming you have $pic_list as an array of all images
# i.e. somethiing like pic_list=`find / -iname "*.jpg"`
for pic in $pic_list
do
display $pic & -OR- eog $pic &
echo "Press 'y' to copy $pic to /home/$USER/<dest_folder>"
read option
if [ $option = "y" -o $option = "Y" ]
then
cp -f $pic /home/$USER/<dest_folder>
else
echo "will not copy $pic"
fi
done
If this is not what you are looking for, pls do let me know.
1. Make symlinks to all the images in a single directory
mkdir all-pics
cd all-pics
find ~/Pictures/ -iname '*.jpg' | \
awk '{name=$0; gsub(/[/]/,"_", name);\
system("ln -s \"" $0 "\" \"" name "\"")}'
Note: The awk script generates and executes the command ln -s "/path/to the/original image.jpg" "_path_to the_original image.jpg" for each image found.
2. Use geeqie to view the images.
3. Use Ctrl+C shortcut to copy the current image to a separate to_be_printed/ folder. geeqie's copy dialog remembers the last selected folder, so you'd only be pressing Ctrl+C, Enter to copy the picture.
There are many ways to solve this problem. I personally always found e.g. qiv a nice tool for some problem like this. You can easily configure it to read a qiv-command config where you can exactly script what you want to do on a particular keypress. I use it for a similar task as you do and just keep my fingers on d (delete), space (next).
e.g.
https://bitbucket.org/ciberandy/qiv/src/3b3fb21db52c076cd05792f648df8ae659d1af92/qiv-command.example

An efficient way to detect corrupted png files?

I've written a program to process a bunch of png files that are generated by a seperate process. The capture mostly works, however there are times when the process dies and is restarting which leaves a corrupted image. I have no way to detect when the process dies or which file it dies one (there are ~3000 png files).
Is there a good way to check for a corrupted png file?
I know this is a question from 2010, but I think this is a better solution: pngcheck.
Since you're on a Linux system you probably already have Python installed.
An easy way would be to try loading and verifying the files with PIL (Python Imaging Library) (you'd need to install that first).
from PIL import Image
v_image = Image.open(file)
v_image.verify()
(taken verbatim from my own answer in this thread)
A different possible solution would be to slightly change how your processor processes the files: Have it always create a file named temp.png (for example), and then rename it to the "correct" name once it's done. That way, you know if there is a file named temp.png around, then the process got interrupted, whereas if there is no such file, then everything is good.
(A variant naming scheme would be to do what Firefox's downloader does -- append .partial to the real filename to get the temporary name.)
Kind of a hack, but works
If you are running on linux or something like you might have the "convert" command
$ convert --help
Version: ImageMagick 5.5.6 04/01/03 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 2003 ImageMagick Studio LLC
Usage: convert [options ...] file [ [options ...] file ...] [options ...] file
If you make an invalid png, and then try to convert, you'll get an error:
$ date> foo.png
$ convert foo.png foo.gif
convert: NotAPNGImageFile (foo.png).
Find all non-PNG files:
find . -type f -print0 | xargs -0 file --mime | grep -vF image/png
Find all corrupted PNG files:
find . -type f -print0 | xargs -0 -P0 sh -c 'magick identify +ping "$#" > /dev/null' sh
file command only checks magic number. Having the PNG magic number doesn't mean it is a well formed PNG file.
magick identify is a tool from ImageMagick. By default, it only checks headers of the file for better performance. Here we use +ping to disable the feature and make identify read the whole file.

Resize a list of images in line command

I would like to resize a list of images, all in the directory. To achieve that, I use convert from imagemagick. I would like to resize
image1.jpg
image2.jpg
...
into
image1-resized.jpg
image2-resized.jpg
...
I was wondering if there is a method to achieve this in a single command line. An elegant solution could be often useful, not only in this case.
EDIT:
I would like a non script-like solution, ie. without for loop.
If you want to resize them to 800x600:
for file in *.jpg; do convert -resize 800x600 -- "$file" "${file%%.jpg}-resized.jpg"; done
(works in bash)
ls *.jpg|sed -e 's/\..*//'|xargs -I X convert X.jpg whatever-options X-resized.jpg
You can eliminate the sed and be extension-generic if you're willing to accept a slightly different final filename, 'resized-image1.jpg' instead of 'image1-resized.jpg':
ls|xargs -I X convert X whatever-options resized-X
GNU Parallel is even easier than for loops, and it's often faster:
parallel convert -resize 800x600 -- "{}" "{.}-resized.jpg" ::: *.jpg
A few things going on here, from right to left:
::: *.jpg means run the command for every jpg file
{.} means insert the current filename without the suffix (.jpg)
{} means insert the current filename
parallel means run the following command many times in parallel. It will choose the max to do in parallel to match the number of cores your computer has. As each one finishes it will launch the next one until all the jpg files are converted.
This runs the command convert --resize 800x600 -- foo.jpg foo-resized.jpg for each file. The -- tells convert to stop processing flags, in case a file name happens to start with a -.
P.S. On my mac I have Homebrew installed, so I was able to install parallel and convert with
brew install parallel
brew install imagemagick
If your image files have different extensions:
for f in *; do convert -resize 800x600 -- "$f" "${f%.*}-resized.${f##*.}"; done

Resources