One liner to remove suffix from files in directory - macos

On OSX 10.8, how can I rename all files in the current directory by removing certain amount of character from the of the name? Example:
my_img_1#2x.png to become my_img_1.png
So only #2x part will be removed.

The exact way to do this depends on your files - for example, are they all PNG files? Do you want to remove #2x from all of them? Here is a loop which will remove the 3 characters before the file extension from all files in the current directory:
for f in *; do
echo mv "$f" $(sed -r 's/(.*).{3}\.([^\.]+)/\1.\2/' <<< "$f")
done
If all your files are named [something]#2x.png and you want them to be renamed [something].png, this will work:
for f in *; do
echo mv "$f" "${f%#*}.png"
done
This works using parameter expansion - effectively the construct ${var%pattern} means "take $var and remove the shortest string that matches [pattern] from the end of it". So in this case, ${f%#*} means "remove the last # and anything after it from $f". Then we append .png to replace the file extension which has been removed as part of that operation.
$f = filename#2x.png
${f%#*} = filename
${f%#*}.png = filename.png

Related

Shell: Rename a file

In my directory I have thousands of PDF files. I want to write a shell script where goes through all the files and trims the last 16 characters and and saves back to the directory without keeping the old filename.
Now:
KUD_1234_Abc_DEF_9055_01.pdf
New:
KUD_1234.pdf
How can I solve that.
Thank you all
To the importance of analyzing and describing a problem properly to find a proper solution.
Here I implement exactly what you ask for:
#!/usr/bin/env sh
for oldname
do
# Capture old file name extension for re-use, by trimming-out the leading
# characters up-to including the dot
extension=${oldname##*.}
# Capture the old name without extension
extensionless=${oldname%.*}
# Compose new name by printing the old file name
# up to its length minus 16 characters
# and re-adding the extension
newname=$(
printf '%.*s.%s\n' $((${#extensionless}-16)) "$extensionless" "$extension"
)
# Demonstrate rename as a dummy
echo mv -- "$oldname" "$newname"
done
Works for your sample case:
mv -- KUD_1234_Abc_DEF_9055_01.pdf KUD_1234.pdf
Will collide not rename this:
mv -- KUD_1234_ooh_fail_666_02.pdf KUD_1234.pdf
Will not work with names shorter than 16 characters:
mv -- notwork.pdf notwork.pdf
Will probably not do what you expect if name has no dot extension:
mv -- foobar foobar.foobar
This should work for you (please backup data before trying):
find -type f | sed -E 's|^(.+)(.{16})(\.pdf)$|\1\2\3\ \1\3|g' | xargs -I f -- bash -c "mv f"
However, it's much easier to do it with python:
import os
os.chdir("/home/tkhalymon/dev/tmp/empty")
for f in os.listdir("."):
name, ext = os.path.splitext(f)
os.rename(f, f"{name[:-16]}{ext}")

Removing unknown / non-specific string after file extension on file names

Trying to remove a string that is located after the file name extension, on multiple files at once. I do not know where the files will be, just that they will reside in a subfolder of the one I am in.
Need to remove the last string, everything after the file extension. File name is:
something-unknown.js?ver=12234.... (last bit is unknown too)
This one (below) I found in this thread:
for nam in *sqlite3_done
do
newname=${nam%_done}
mv $nam $newname
done
I know that I have to use % to remove the bit from the end, but how do I use wildcards in the last bit, when I already have it as the "for any file" selector?
Have tried with a modifies bit of the above:
for nam in *.js*
do
newname=${ nam .js% } // removing all after .js
mv $nam $newname
done
I´m in MacOS Yosemite, got bash shell and sed. Know of rename and sed, but I´ve seen only topics with specific strings, no wildcards for this issue except these:
How to rename files using wildcard in bash?
https://unix.stackexchange.com/questions/227640/rename-first-part-of-multiple-files-with-mv
I think this is what you are looking for in terms of parameter substitution:
$ ls -C1
first-unknown.js?ver=111
second-unknown.js?ver=222
third-unknown.js?ver=333
$ for f in *.js\?ver=*; do echo ${f%\?*}; done
first-unknown.js
second-unknown.js
third-unknown.js
Note that we escape the ? as \? to say that we want to match the literal question mark, distinguishing it from the special glob symbol that matches any single character.
Renaming the files would then be something like:
$ for f in *.js\?ver=*; do echo "mv $f ${f%\?*}"; done
mv first-unknown.js?ver=111 first-unknown.js
mv second-unknown.js?ver=222 second-unknown.js
mv third-unknown.js?ver=333 third-unknown.js
Personally I like to output the commands, save it to a file, verify it's what I want, and then execute the file as a shell script.
If it needs to be fully automated you can remove the echo and do the mv directly.
for x in $(find . -type f -name '*.js*');do mv $x $(echo $x | sed 's/\.js.*/.js/'); done

How do I rename multiple files before the extension in linux?

I want to take a group of files with names like 123456_1_2.mpg and turn it into 123456.mpg how can I do this using terminal commands?
To loop over all the available files you can use a for loop over the file names of the form ??????_?_?.mpg.
To rename the files you can retain the shortest match of a pattern from the beginning of the string using ${MYVAR%%pattern} without using any external command.
This said, your code should look like:
#!/bin/bash
shopt -s nullglob # do nothing if no matches found
for file in ??????_?_?.mpg; do
[[ -f $file ]] || continue # skip if not a regular file
new_file="${file%%_*}.mpg" # compose the new file name
echo mv "$file" "$new_file" # remove echo after testing
done
rename 's/_.*/.mpg/' *mpg
this will remove everything between the first underscore and the mpg file extension for all files ending in mpg
We can use grep to strip out everything but the first sequence of numbers. The --interactive flag will ask you if you're sure for each move, so you can make sure it's not doing anything you don't expect.
for file in *.mpg; do
mv --interactive "$file" "$(grep -o '^[0-9]\+' <<< "$file")".mpg
done
The regex ^[0-9]\+ translates to "any sequence of characters that starts with a number and is followed by zero or more numbers".

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

bash rename files with prefix serial number

I have loads of files in a folder. I want to do two things:
prefix them with xxx three digit serial numbers - ascending: 001 002 and so on
remove the prefix from their names, so 001a.xyz = a.xyz
I intend to do this using a simple bash script. What's the most elegant and simple to understand way to do this?
edit
the files are on a removable device, and I cannot seem to set chmod +X on the script on the device. So how do I run a script from my home directory which will change the files in another directory?
To add prefixes:
counter=1
for f in *; do
printf -v prefix_str '%03d' "$((counter++))"
mv "$f" "${prefix_str}$f"
done
To remove prefixes (caution -- this may overwrite if you have two files with the same suffix but different prefixes):
for f in [0-9][0-9][0-9]*; do
mv "$f" "${f:3}"
done
Use mv -n to avoid overwriting when two files have the same suffix.
This should work:
#!/bin/bash
count=1
for file in *; do
if [[ $file =~ [0-9][0-9][0-9].* ]]; then
sfile="${file:3}"
new=$(printf "%03d" ${count})
mv "$file" "${new}${sfile}"
((count++))
else
new=$(printf "%03d" ${count})
mv "$file" "${new}${file}"
((count++))
fi
done
What this script does is, checks for a given file in the current directory. If the file has a prefix already it will remove it and assign a new sequential prefix. If the file has no prefix it will add a sequential prefix to it.
The end result should be, all the files in your current directory (some with and some without prefixes) will have a new sequential prefixes.

Resources