Get basename of files in folder [duplicate] - bash

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

Related

convert png files to pdf with Bash? [duplicate]

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

How to execute command on files with spaces in them? [duplicate]

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.

TCropping images with loop in imagemagick

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.

shell script to change .pdf files to .png

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.

Generating the output file name from the input file in bash

I have a bash script:
#!/bin/bash
convert "$1" -resize 50% "$2"
Instead of passing two arguments while the script is run I want to mention just the source (or input file name) and the output file name should be auto-genarated from the source file name. Something like this "$1" | cut -d'.' -f1".jpg". If the input file name was myimage.png, the output name should be myimage.jpg. .jpg should be appended to the fist part of the source file name. It should also work if the argument is: *.png. So how can I modify my script?
The expansion ${X%pattern} removes pattern of the end of $X.
convert "$1" -resize 50% "${1%.*}.jpg"
To work on multiple files:
for filename ; do
convert "$filename" -resize 50% "${filename%.*}.jpg"
done
This will iterate over each of the command line arguments and is shorthand for for filename in "$#". You do not need to worry about checking whether the argument is *.png - the shell will expand that for you - you will simple receive the expanded list of filenames.
convert "$1" -resize 50% "${1%.*}.jpg"
The magic is in the %.* part, which removes everything after the last dot. If your file is missing an extension, it will still work (as long as you don't have a dot anywhere else in the path).
OUTFILE=`echo $1|sed 's/\(.*\)\..*/\1/'`.jpg
convert "$1" -resize 50% "$OUTFILE"

Resources