File name manipulation with shell scripts: changing file extensions - shell

I need to write a shell script to convert the image format from .png to .tif. The script is as follows:
#!/bin/sh
for f in `ls *.png`
do
convert $f $f.tif
done
But doing this will append the .tif format to the existing filename. ie if the image is abc.png the $f will have abc.png and after converting the filename becomes abc.png.tif. This is not what I want. I need it to be abc.tif. How do I manipulate $f to remove .png?

This should work for you:
#!/bin/bash
for file in *.png
do
filename=$(basename "$file")
filename=${filename%.*}
convert $file $filename.tif
done
A line-by-line walkthrough of how this works:
for file in *.png - you don't need command substitution ls *.png to get the list of files with png extension. The wildcard * will auto-expand in shell to match the list of files in cwd; and in this case the list of files ending in .png.
filename=$(basename "$file") - this is only for defensive programming; it gets the actual name of the file
filename=${filename%.*} - this removes the extension from filename
convert $file $filename.tif - runs your actual convert command

Related

Bash For Loop: Convert URL to .jpg File and Retain URL id in Image Filename

I am new to bash. I've written the following for loop to convert an array of URLs to image files using basename:
for i in ${img_jpg[#]//\"/} ; do convert "$i" -set filename:base "%[basename]" "%[filename:base].png" ; done
It's working well for URLs that are formatted like this:
https://xxx.domain.com/xy22233
Now I would like to create a similar loop to that uses basename without the file extension for urls like this:
https://img.domain.com/vi/y2x112233/default.jpg
I would like to output a file with just this part of the URL:
y2x112233
Or even the entire URL.
This is my attempt but it hasn't worked:
for i in ${img_jpg[#]//\"/} ; do convert "$i" "${filename%.*}" ; done
Here is one way to do the loop with ImageMagick convert.
filelist="https://img.youtube.com/vi/--1uHerc2Ns/hqdefault.jpg"
for file in $filelist; do
dir=$(dirname $file)
base=$(basename $dir)
echo $dir
echo $base
echo $file
convert $file $base.jpg
done
Change your "for" loop at desired.

How can I collect and format filename, size and extension of directory contents in bash?

I am looking to output a list of contents formatted by size, type of each file and the file name without the extension.
I've tried using ls grep and awk but to no avail. I'm not sure how else this could be done, could someone help me?
I'm looking for the output to show as below;
Filename FileSize Extension
Filename FileSize Extension
Filename FileSize Extension
Filename FileSize Extension
and so on.
There's no need for ls here (and, in fact, good reason not to use it; see Why you shouldn't parse the output of ls).
As native bash:
#!/usr/bin/env bash
for file in *; do # iterate over contents of current directory
[[ -f "$file" ]] || continue # skip things that aren't files
size=$(wc -c <"$file") # collect size
# ...and actually format and print metadata
printf '%20q %20s %q\n' "${file%.*}" "$size" "${file##*.}"
done
Here, %q is used to ensure that even names with nonprintable components are formatted unambiguously (so you can tell the difference between a name with spaces and a name with tabs, for example).

How can I iterate over the contents of a directory in unix without using a wildcard?

I totally understand what the problem is here.
I have a set of files, prepended as 'cat.jpg' and 'dog.jpg.' I just want to move the 'cat.jpg' files into a directory called 'cat.' Same with the 'dog.jpg' files.
for f in *.jpg; do
name=`echo "$f"|sed 's/ -.*//'`
firstThreeLetters=`echo "$name"|cut -c 1-3`
dir="path/$firstThreeLetters"
mv "$f" "$dir"
done
I get this message:
mv: cannot stat '*.jpg': No such file or directory
That's fine. But I can't find any way to iterate over these images without using that wildcard.
I don't want to use the wildcard. The only files are prepended with the 'dog' or 'cat'. I don't need to match. All the files are .jpgs.
Can't I just iterate over the contents of the directory without using a wildcard? I know this is a bit of an XY Problem but still I would like to learn this.
*.jpg would yield the literal *.jpg when there are no matching files.
Looks like you need nullglob. With Bash, you can do this:
#!/bin/bash
shopt -s nullglob # makes glob expand to nothing in case there are no matching files
for f in cat*.jpg dog*.jpg; do # pick only cat & dog files
first3=${f:0:3} # grab first 3 characters of filename
[[ -d "$first3" ]] || continue # skip if there is no such dir
mv "$f" "$first3/$f" # move
done

shell script, replace .png with #2x.png

I need a scrip to replace .png with #2x.png How can I do this for all file names in a directory?
Assuming bash:
for f in *.png; do
mv "$f" "${f%.png}#2x.png"
done
Explanation:
Line 1:
for f in *.png; do
For loop. Iterates over all filenames in the current directory that match the glob pattern *.png, sets $f to each name in turn, and executes the body of the loop.
Line 2:
mv "$f" "${f%.png}#2x.png"
Executes the mv tool. The first arg is just "$f", which is the file you want to rename. The second arg is a two-step process:
${f%.png} evaluates to the filename with the extension .png stripped off.
Following the stripped filename is #2x.png, which just appends that string back onto the filename.
Line 3:
done
Terminates the for loop.
The following script should work for you:
#!/bin/bash
for file in *.png
do
filename=$(basename "$file")
filename="${filename%.*}"
filename="${filename}#2x.png"
mv "$file" "$filename"
done

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