I have a bunch of files in the format photo.jpg.png in a folder, and for every photo in this folder, I want to replace the .jpg.png with .png. How can I do this from Terminal?
I have a basic Python and bash background, so I know I'd want to do something like this:
$ for i in *.png; do mv $i $i[:-8]; done
$ for i in *; do mv $i $i.png; done
But what would I replace the Pythonic [:-8] with in order to remove the last 8 characters of each filename?
EDIT I now realize that a substring that counts from the end of the string would be superior. Is there a way to do this as well?
You can use pattern expansion:
for f in *.jpg.png; do mv -v "$f" "${f/.jpg.png/.png}"; done
Though you might still have problems with a filename like foo.jpg.png.gif.
If you really want to strip the last 7 or 8 characters, you can use a substring expansion:
for f in *.jpg.png; do mv -v "$f" "${f:0:-7}png"; done
Note that use of negative numbers in substring length requires bash version 4 or higher.
With Perl's standalone rename command:
rename -n 's/jpg\.png$/png/' *.jpg.png
or
rename -n 's/.......$/png/' *.jpg.png
Output:
rename(photo.jpg.png, photo.jpg)
If everything looks okay, remove `-n'.
rename is designed for this kinda thing;
$ rename .jpg.png .png *.jpg.png
For MacOS, I realized that rename may not be available by default, you can install it using brew.
$ brew install rename
and then use -s option for rename;
$ rename -s .jpg.png .png *.jpg.png
Removing the last 8 characters using perl's rename :
$ rename -n 's/.{8}$//' *.png
(remove -n switch when your tests are OK)
or with bash :
for i in *.png; do
echo mv "$i" "${i:0:-8}"
done
(remove echo when your tests are OK)
There are other tools with the same name which may or may not be able to do this, so be careful.
If you run the following command (GNU)
$ file "$(readlink -f "$(type -p rename)")"
and you have a result like
.../rename: Perl script, ASCII text executable
and not containing:
ELF
then this seems to be the right tool =)
If not, to make it the default (usually already the case) on Debian and derivative like Ubuntu :
$ sudo update-alternatives --set rename /path/to/rename
(replace /path/to/rename to the path of your perl's rename command.
If you don't have this command, search your package manager to install it or do it manually
Last but not least, this tool was originally written by Larry Wall, the Perl's dad.
Something like that:
$ v=test.jpg.png
$ echo ${v:0:-8}
test
This should work:
for file in *.jpg.png; do mv $file ${file//jpg.png/jpg} ; done
Related
I have many nested folders of json language files, such as
da-dk.json
de-de.json
en-us.json
I need to change them all to capitalize the letters after the hyphen, as in
da-DK.json
de-DE.json
en-US.json
I am on a Mac with zsh. I originally thought I could do it with a GUI utility I have used called A Better Finder Rename but it apparently does not offer case conversions on replace.
I know regex and figured it would be something like find
^([a-z]{2})-([a-z]{2}) and replace with $1-\U$2 but I'm not sure how to do this in the command line.
Given that you are using ZSH shell, you can use the awesome zmv command
zmv '(**/)(*)-(*).json' '${1}${2}-$3:u.json'
You may need to autoload zmv before running the above command.
Short explanation:
(**/) takes care of nested folders which is mapped to ${1}
First (*) matches the part before hyphen and is mapped to ${2}
Second (*) matches the part after hyphen and is uppercased by :u before being mapped to ${3}.
There are some useful material in this SO question and its answers.
In traditional shell commands:
for i in *.json; do
echo mv "$i" "${i:0:3}$(tr '[[:lower:]]' '[[:upper:]]' <<< ${i:3:2}).json"
done
Drop echo when the output looks good.
With perl rename:
install via homebrew (if not already installed):
brew install rename
command:
rename -n 's/\w{2}(?=\.)/uc $&/e' *.json
Drop -n switch when the output looks good.
On building apps with the Angular 2 CLI, I get outputs which are named, for instance:
inline.d41d8cd.bundle.js
main.6d2e2e89.bundle.js
etc.
What I'm looking to do is create a bash script to rename the files, replacing the digits between the first two . with some given generic string. Tried a few things, including sed, but I couldn't get them to work. Can anyone suggest a bash script to get this working?
In pure bash regEx using the =~ variable (supported from bash 3.0 onwards)
#!/bin/bash
string_to_replace_with="sample"
for file in *.js
do
[[ $file =~ \.([[:alnum:]]+).*$ ]] && string="${BASH_REMATCH[1]}"
mv -v "$file" "${file/$string/$string_to_replace_with}"
done
For your given input files, running the script
$ bash script.sh
inline.d41d8cd.bundle.js -> inline.sample.bundle.js
main.6d2e2e89.bundle.js -> main.sample.bundle.js
Short, powerfull and efficient:
Use this (perl) tool. And use Perl Regular Expression:
rename 's/\.\X{4,8}\./.myString./' *.js
or
rename 's/\.\X+\./.myString./' *.js
A pure-bash option:
shopt -s extglob # so *(...) will work
generic_string="foo" # or whatever else you want between the dots
for f in *.bundle.js ; do
mv -vi "$f" "${f/.*([^.])./.${generic_string}.}"
done
The key is the replacement ${f/.*([^.]./.${generic_string}.}. The pattern /.*([^.])./ matches the first occurrence of .<some text>., where <some text> does not include a dot ([^.]) (see the man page). The replacement .${generic_string}. replaces that with whatever generic string you want. Other than that, double-quote in case you have spaces, and there you are!
Edit Thanks to F. Hauri - added -vi to mv. -v = show what is being renamed; -i = prompt before overwrite (man page).
I have images files that when they are created have these kind of file names:
Name of file-1.jpg
Name of file-2.jpg
Name of file-3.jpg
Name of file-4.jpg
..etc
This causes problems for sorting between Windows and Cygwin Bash. When I process these files in Cygwin Bash, they get processed out of order because of the differences in sorting between Windows file system and Cygwin Bash sees them. However, if the files get manually renamed and numbered with leading zeroes, this issue isn't a problem. How can I use Bash to rename these files automatically so I don't have to manually process them. I'd like to add a few lines of code to my Bash script to rename them and add the leading zeroes before they are processed by the rest of the script.
Since I use this Bash script interchangeably between Windows Cygwin and Mac, I would like something that works in both environments, if possible. Also all files will have names with spaces.
You could use something like this:
files="*.jpg"
regex="(.*-)(.*)(\.jpg)"
for f in $files
do
if [[ "$f" =~ $regex ]]
then
number=`printf %03d ${BASH_REMATCH[2]}`
name="${BASH_REMATCH[1]}${number}${BASH_REMATCH[3]}"
mv "$f" "${name}"
fi
done
Put that in a script, like rename.sh and run that in the folder where you want to covert the files. Modify as necessary...
Shamelessly ripped from here:
Capturing Groups From a Grep RegEx
and here:
How to Add Leading Zeros to Sequential File Names
#!/bin/bash
#cygcheck (cygwin) 2.3.1
#GNU bash, version 4.3.42(4)-release (i686-pc-cygwin)
namemodify()
{
bname="${1##*/}"
dname="${1%/*}"
mv "$1" "${dname}/00${bname}" # Add any number of leading zeroes.
}
export -f namemodify
find . -type f -iname "*jpg" -exec bash -c 'namemodify "$1"' _ {} \;
I hope this won't break on Mac too :) good luck
I have a bunch of files (more than 1000) on this like the followings
$ ls
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-dev.lc
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-dev.lex
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-train.lc
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm-train.lex
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm.lc
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm.lex
org.allenai.ari.solvers.termselector.ExpandedLearner.lc
org.allenai.ari.solvers.termselector.ExpandedLearner.lex
org.allenai.ari.solvers.termselector.ExpandedLearnerSVM.lc
org.allenai.ari.solvers.termselector.ExpandedLearnerSVM.lex
....
I have to rename these files files by adding a learners right before the capitalized name. For example
org.allenai.ari.solvers.termselector.BaselineLearnersurfaceForm.lex
would change to
org.allenai.ari.solvers.termselector.learners.BaselineLearnersurfaceForm.lex
and this one
org.allenai.ari.solvers.termselector.ExpandedLearner.lc
would change to
org.allenai.ari.solvers.termselector.learners.ExpandedLearner.lc
Any ideas how to do this automatically?
for f in org.*; do
echo mv "$f" "$( sed 's/\.\([A-Z]\)/.learner.\1/' <<< "$f" )"
done
This short loop outputs an mv command that renames the files in the manner that you wanted. Run it as-is first, and when you are certain it's doing what you want, remove the echo and run again.
The sed bit in the middle takes a filename ($f, via a here-string, so this requires bash) and replaces the first occurrence of a capital letter after a dot with .learner. followed by that same capital letter.
There is a tool called perl-rename, sometimes rename. Not to be confused with rename from util-linux.
It's very good for tasks like this as it takes a perl expression and renames accordingly:
perl-rename 's/(?=\.[A-Z])/.learners/' *
You can play with the regex online
Alternative you can a for loop and $BASH_REMATCH:
for file in *; do
[ -e "$file" ] || continue
[[ "$file" =~ ^([^A-Z]*)(.*)$ ]]
mv -- "$file" "${BASH_REMATCH[1]}learners.${BASH_REMATCH[2]}"
done
A very simple approach (useful if you only need to do this one time) is to ls >dummy them into a text file dummy, and then use find/replace in a text editor to make lines of the form mv xxx.yyy xxx.learners.yyy. Then you can simple execute the resulting file with ./dummy.
The exact find/replace commands depend on the text editor you use, but something like
replace org. with mv org.. That gets you the mv in the beginning.
replace mv org.allenai.ari.solvers.termselector.$1 with mv org.allenai.ari.solvers.termselector.$1 org.allenai.ari.solvers.termselector.learner.$1 to duplicate the filename and insert the learner.
There is also syntax with a for, which can do it probably in one line, (long) but I cannot explain it - try help for if you want to learn about it.
I have a folder with a series of files named:
prefix_1234_567.png
prefix_abcd_efg.png
I'd like to batch remove one underscore and the middle content so the output would be:
prefix_567.png
prefix_efg.png
Relevant but not completely explanatory:
How can I batch rename files using the Terminal?
Regex to batch rename files in OS X Terminal
In your specific case you can use the following bash command (bash is the default shell on macOS):
for f in *.png; do echo mv "$f" "${f/_*_/_}"; done
Note: If there's a chance that your filenames start with -, place -- before them[1]:
mv -- "$f" "${f/_*_/_}"
Note: echo is prepended to mv so as to perform a dry run. Remove it to perform actual renaming.
You can run it from the command line or use it in a script.
"${f/_*_/_}" is an application of bash parameter expansion: the (first) substring matching pattern _*_ is replaced with literal _, effectively cutting the middle token from the name.
Note that _*_ is a pattern (a wildcard expression, as also used for globbing), not a regular expression (to learn about patterns, run man bash and search for Pattern Matching).
If you find yourself batch-renaming files frequently, consider installing a specialized tool such as the Perl-based rename utility.
On macOS you can install it using popular package manager Homebrew as follows:
brew install rename
Here's the equivalent of the command at the top using rename:
rename -n -e 's/_.*_/_/' *.png
Again, this command performs a dry run; remove -n to perform actual renaming.
Similar to the bash solution, s/.../.../ performs text substitution, but - unlike in bash - true regular expressions are used.
[1] The purpose of special argument --, which is supported by most utilities, is to signal that subsequent arguments should be treated as operands (values), even if they look like options due to starting with -, as Jacob C. notes.
To rename files, you can use the rename utility:
brew install rename
For example, to change a search string in all filenames in current directory:
rename -nvs searchword replaceword *
Remove the 'n' parameter to apply the changes.
More info: man rename
You could use sed:
ls * | sed -e 'p;s#_.*_#_#g' | xargs -n2 mv
result:
prefix_567.png prefix_efg.png
*to do a dry-run first, replace mv at the end with echo
Explanation:
e: optional for only 1 sed command.
p: to print the input to sed, in this case it will be the original file name before any renaming
#: is a replacement of / character to make sed more readable. That is, instead of using sed s/search/replace/g, use s#search#replace#g
_.* : the underscore is an escape character to refer to the actual '.' character zero or more times (as opposed to ANY character in regex)
-n2: indicates that there are 2 outputs that need to be passed on to mv as parameters. for each input from ls, this sed command will generate 2 output, which will then supplied to mv.
I had a batch of files that looked like this: be90-01.png and needed to change the dash to underscore. I used this, which worked well:
for f in *; do mv "$f" "`echo $f | tr '-' '_'`"; done
you can install rename command by using brew. just do brew install rename and use it.
Using mmv
mmv '*_*_*' '#1_#3' *.png
try this
for i in *.png ; do mv "$i" "${i/remove_me*.png/.png}" ; done
Here is another way:
for file in Name*.png; do mv "$file" "01_$file"; done
Since programmatically renaming files is risky (potentially destructive if you get it wrong), I would use a tool with a dry run mode built specifically for bulk renaming, e.g. renamer.
This command operates on all files in the current directory, use --dry-run until you're confident the output looks correct:
$ renamer --find "/(prefix_)(\w+_)(\w+)/" --replace "$1$3" -e name --dry-run *
Dry run
✔︎ prefix_1234_567.png → prefix_567.png
✔︎ prefix_abcd_efg.png → prefix_efg.png
Rename complete: 2 of 2 files renamed.
Plenty more renamer usage examples here.