This question already has answers here:
How to convert all png files to pdf in Bash?
(2 answers)
Closed 2 years ago.
for f in find *.png; do convert "$f" "$f".pdf; done
This is what I have to find the png files in the directory and convert them to pdf, but I get errors. What is a better way to do this in Bash?
convert: unable to open image `find': No such file or directory # error/blob.c/OpenBlob/2705.
convert: no decode delegate for this image format `' # error/constitute.c/ReadImage/504.
convert: no images defined `find.pdf' # error/convert.c/ConvertImageCommand/3257.
If you're working in just one directory and not requiring find, you can do the following:
for i in *.png; do convert "$i" "${i%.png}.pdf"; done
which uses the shell globbing to find your files. Note the variable substitution to convert from a png to a pdf extension.
Otherwise it's more complicated. I think your find args are not correct. I would try:
find . -name \*.png
Note that I specify the starting directory (.) and then the name pattern (via -name). You need to escape the glob (asterisk) such that the shell doesn't expand it, and instead passes it directly to find.
Now, you can then execute find in a subshell, and then use the results.
e.g.
for f in $(find . -name \*.png); do convert "$f" "$f".pdf; done
Note the $(...) which executes the subshell and makes the output available.
If your filenames contain whitespace, the shell may split on this and cause you further problems. If this is the case there are a number of options presented here
Related
This question already has answers here:
Extract filename and extension in Bash
(38 answers)
Closed 12 months ago.
I want to compress image files and wrote the following bash script:
for i in *.png; do convert $i -quality 100% $i-comp.jpeg; done;
When I run it I get filenames like 48.png-comp.jpeg. I only want to have like 48-comp.jpeg, so basically removing the .png part of the filename.
I tried using this ${$i/.png/}, which gives me an error message.
Any suggestions how to do this?
${$i/.png/} is almost correct, but the second $ is not needed inside a Parameter Expansion.
#!/bin/bash
for i in *.png; do
convert "$i" -quality 88% "${i/.png/}-comp.jpeg"
done
Note: ${i%.png} is commonly more used to remove a file extension than ${i/.png/}, but both should produce the same output.
You could use parameter expansion to strip the png extension for the output file name:
for i in *.png; do convert $i -quality 100% ${i%.png}-comp.jpeg; done
This question already has answers here:
How to loop through file names returned by find?
(17 answers)
Closed 3 years ago.
I am dealing with a legacy codebase where we're trying to convert all jpeg/png files to webp format using the cwebp command. Unfortunately, a lot of the image files were saved with spaces in the name.
Example: i am poorly named.jpg
So when running the following bash script to find all jpegs in the directory and loop through and convert them the words separated by spaces are treated as another file so the image never gets converted.
We don't want to remove the whitespaces, but just create a webp file with the exact same name.
files=$(find ./ -type f -name "*.jpg")
for jpg in $files
do
webp="${jpg/%jpg/webp}";
if [ ! -f $webp ]; then
echo "The webp version does not exist";
cwebp -q 80 "$jpg" -o "$webp";
fi
done
I've tried placing jpg=$(printf '%q' "$jpg") immediately after the do in the above code as well as other things.
I expect i am poorly named.webp to be created if file i am poorly named.jpg exists.
But there is no real reason to store all filenames. So an alternative is:
find ./ -type f -name "*.jpg" | while read jpg
do
....
But this works only, if a filename contains no linefeed. For files with linesfeeds there are other solutions. Ask for it if needed.
This question already has answers here:
How can I use a file in a command and redirect output to the same file without truncating it?
(14 answers)
Closed 7 years ago.
I have many directories with multiple files, each following strict naming convention. I tried this loop:
for s in /home/admin1/phd/results_Sample_*_hg38_hg19/Sample_*_bwa_LongSeed_sorted_hg38.bam; do
bedtools genomecov -ibam $s > $s
done
but get for every file:
Failed to open file
I just can't see the mistake
Note that if you are redirecting the output to the input file, bash will truncate the file before the process starts. Like this:
cat file > file
will result an empty file. Meaning you'll loose it's contents.
This is because bash will open all files which are subject of io redirection before it starts the command. If you are using > bash will additionally truncate the file to zero length. I don't know bedtools but I guess it expects non-empty files as input, that's why the message.
About the loop. Usually it is not a good idea to loop over the results of a glob expression like you did. This is because file names may contain the internal field separator of bash which would lead to inconsistent results.
I would suggest to use find instead:
find /home/admin1/phd/results_Sample_*_hg38_hg19 \
-maxdepth 1 \
-name 'Sample_*_bwa_LongSeed_sorted_hg38.bam' \
-exec bash -c 'bedtools genomecov -ibam "{}" > "{}.copy"' \;
A pretty basic question but I'm new to
Imagemagick (and bash) and I'm having trouble batch cropping images in a folder. I've tried using a loop:
for image in '/home/donald/Desktop/New Folder'*.jpg; do
convert "$image" -gravity center -crop 95X95% "${image%.jpg}"-modified.jpg
done
but it returns:
convert.im6: unable to open image `/home/donald/Desktop/New Folder/*.jpg': No such file or directory # error/blob.c/OpenBlob/2638.
convert.im6: no images defined `/home/donald/Desktop/New Folder/*-modified.jpg' # error/convert.c/ConvertImageCommand/3044."
What would be the proper way of doing this?
Edit: Apparently a space in the folder name was causing problems I deleted it and things seem to be working.Apparently if you want to use a folder with a space name in bash you need to escape the space.
I believe you have no jpg files in the /home/donald/Desktop/New Folder/ directory. The shell will interpret it as the literal string /home/donald/Desktop/New Folder/*.jpg if there are no files matching the wildcard-ed string.
See this example:
$ for f in *.jpg*; do echo $f; done
file.jpg
file2.jpg
$ for f in *.jpgg; do echo $f; done
*.jpgg
See how that last one is the literal string and not a real file? It should have been displayed the first time too if it was (notice the trailing asterix symbol in *.jpg*).
You can fix this by checking if the file exists before executing the command, using [ -f "${file}" ]. For instance:
for image in '/home/donald/Desktop/New Folder'*.jpg; do
[ -f "${image}" ] && convert "$image" -gravity center -crop 95X95% "${image%.jpg}"-modified.jpg
done
This will check if the file image exists (-f) and execute the following statement only if true is returned &&. Had you written || instead of && then the following statement would be executed when false was returned.
Note that bash doesn't return true or false but it's the easiest way to explain and comprehend the notation.
Not sure how to ask a question about a previous posted question so if I should just add to the previous question somehow feel free to tell me.
Anyway, I posted a question yesterday
shell script to change .pdf files to .png Mac OS 10.3
sips does work and if I do it on the command line one file at a time it works but my for loop isn't working
heres what I got
for pdf in *{pdf,PDF} ; do
sips -s format png --out "${pdf%%.*}.png" "$pdf"
done
and its saying
Warning: *{pdf, not a valid file - skipping
Error 4: no file was specified
Try 'sips --help' for help using this tool
thanks
Looks fine to me. Are you sure you are using bash to execute this script and not /bin/sh?
Make sure your first line is:
#! /bin/bash
Try echoing the files and see if it works:
for pdf in *{pdf,PDF} ; do
echo "$pdf"
done
If your shell is bash you can do this
shopt -s nullglob
This changes the behavior of bash when no globs match. Normally if you say *pdf and there are no files ending in "pdf" it will return a literal asterisk instead. Setting nullglob makes bash do what you would expect and return nothing in such a case.
Alternately, and more robustly, you could do it this way
for pdf in *[pP][dD][fF] ; do
sips -s format png --out "${pdf%%.*}.png" "$pdf"
done
Which should work without nullglob being set and in all shells that support parameter substitution with this syntax. Note that this is still not robust on case sensitive filesystems due to a risk of name collision if you have two PDF files whose names differ only due to the case of the extension. To handle this case properly you could do
for pdf in *[pP][dD][fF] ; do
sips -s format png --out "${pdf%%.*}.$(tr pPdDfF pPnNgG <<<"${pdf#*.}")" "$pdf"
done
This should be sufficiently robust.
EDIT: Updated to correct incorrect $pdf expansion in the extension.
UPDATED2
#!/bin/bash
# Patters patching no files are expanded into null string
# this will allow us to make this script work when no files
# exist in this directory with this extension
shopt -s nullglob
# Consider only the lowercase 'pdf' extensions and make them lowercase 'png'
for b in *.pdf
do
c="$b"
b="${b/.pdf/}"
convert"$c" "$b.png"
done
# Consider only the uppercase 'pdf' extensions and make them uppercase 'png'
for b in *.PDF
do
c="$b"
b="${b/.PDF/}"
convert "$c" "$b.PNG"
done
Note that the convert program is a part of the ImageMagick program.