How to batch rename files in a terminal? - macos

I want to rename the files in a directory to sequential numbers.
stars_01_012.png
stars_01_014.png
stars_01_015.png
stars_01_017.png
stars_02_012.png
stars_02_014.png
stars_02_015.png
stars_02_017.png
And change it into
stars_01_001.png
stars_01_002.png
stars_01_003.png
stars_01_004.png
stars_02_001.png
stars_02_002.png
stars_02_003.png
stars_02_004.png
Relatives but not completely:
How to Batch Rename Files in a macOS Terminal?
How can I batch rename files using the Terminal?

You can do it with rename which you can install on macOS with homebrew using:
brew install rename
The command then looks like this:
rename --dry-run -X -e 'my #parts=split /_/; my $this=$parts[0].$parts[1]; if(!defined($ENV{previous}) || $this ne $ENV{previous}){$ENV{previous}=$this; $ENV{N}=0}; $ENV{N}=$ENV{N}+1; $_ = $parts[0]."_".$parts[1]."_".$ENV{N}' *png
Sample Output
'stars_01_012.png' would be renamed to 'stars_01_1.png'
'stars_01_014.png' would be renamed to 'stars_01_2.png'
'stars_01_015.png' would be renamed to 'stars_01_3.png'
'stars_01_017.png' would be renamed to 'stars_01_4.png'
'stars_02_012.png' would be renamed to 'stars_02_1.png'
'stars_02_014.png' would be renamed to 'stars_02_2.png'
'stars_02_015.png' would be renamed to 'stars_02_3.png'
'stars_02_017.png' would be renamed to 'stars_02_4.png'
'stars_88_099.png' would be renamed to 'stars_88_1.png'
Explanation:
my #parts=split /_/ splits the filename into 3 parts using the underscore as the separator,
my $this=$parts[0].$parts[1] saves the first two parts simply concatenated together, e.g. "stars01",
the if statement tests if either of the first two parts have changed, then
$ENV{previous}=$this; $ENV{N}=0 saves the current stem of the filename in an environment variable called previous and the current sequential counter in another environment variable N,
$ENV{N}=$ENV{N}+1 increments the sequential counter, and
$_ = $parts[0]."_".$parts[1]."_".$ENV{N} creates the new output filename from the various parts.
If that all looks correct, remove the --dry-run and run it again - probably in a spare directory with a copy of your files until you are sure all is ok :-)
The above may be easier to read like this:
#!/bin/bash
rename --dry-run -X -e '
my #parts=split /_/; # split filename into parts based on underscore
my $this=$parts[0].$parts[1]; # save first two parts of filename in $this
# see if it is a new stem
if(!defined($ENV{previous}) || $this ne $ENV{previous}){
$ENV{previous}=$this; $ENV{N}=0}; # save new initial part, reset N
$ENV{N}=$ENV{N}+1; # increment N
$_ = $parts[0]."_".$parts[1]."_".$ENV{N} # formulate new filename from parts
' *png
Change the last line to the following if you want to zero-pad the numbers out to three digits:
$_ = sprintf("%s_%s_%03d",$parts[0],$parts[1],$ENV{N}) # formulate new filename from parts
Note:
I save the previous file prefix and sequential counter into environment variables to preserve them between files - there may be easier ways - if anyone knows, please ping me! Thanks.

You can also create an applescript script, using the Terminal commandline.
below a script to copy in the script editor
set thefile to do shell script "ls The_path_of_your_files" that you wish to rename '"with in common the extension (in your example .png) which gives:
**set thefile to do shell script "ls The_path_of_your_files/*.png"
set AppleScript's text item delimiters to return
set chFile to text items of thefile
set AppleScript's text item delimiters to ""
set n to count chFile
repeat with i from 1 to n
set rnFile to item i of chFile
set nwname to (do shell script "echo" & quoted form of rnFile & "| sed 's # stars_01_01 # stars_01_00 #'")
set endFile to (do shell script "mv -f" & quoted form of rnFile & "" & quoted form of nwname)
end repeat**
which is equivalent to the rename multiple files function of the Finder

Just a rough answer, why use terminal script?
You can just use the Finder with its rename function
by selecting the common part of all your files in your example "stars_01_01" and replacing it with "stars_01_00", this will lead to the same result without having to write a script with :
sed 's#stars_01_01#stars_01_00#g'

Related

how generate & use list of values within a commend?

I would like to use a windows command that is processing many files.
The syntax of the command is as follows & requires a separated list of filenames: command "file_1 file_2 file_3 file_4" output-file
I have to handle 1000s of files.
Is there any way generate the list of files automatically in the command line?
Something like:
command "(echo file_1.txt to file_1000.txt)" output-file
Thanks a lot!
If your question is "how do I create a list of files numbered 1 to 1000," then you can do this in PowerShell:
1..1000 | % { New-Item file_$_.txt }
Note that % is an alias for ForEach-Object. The $_ token means "current object from the pipeline" (i.e., the number 1-1000).
This question has many open-ended issues. It is unlikely that all files can be processed by a single command since cmd has a line length limit. However, you can process them one at a time. It is unclear what the output-file would contain.
FOR %A IN ("file_*") DO (command "%A")
If this is in a .bat file script, double the PERCENT character on the variable name.
FOR %%A IN ("file_*") DO (command "%%~A")

fish shell how to tokenize variable to a list

It's weird that others are complaining that fish is always splitting their variables to lists. But to me it's just having the multiline variable as a single string.
I'm trying to write a nautilus script. The nautilus should set a variable called $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS with the selected files separated with newlines.
I'm trying to get them as a list to loop over them with fish. But they just behave as a single element.
set -l files $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
for i in (seq (count $files))
echo (count $files) >> koko
end
and the file koko now shows the number 1.
Fish does not split variables after they have been set (this is known as "word splitting").
What it does, however, do is split command substitutions on newlines, so
set files (echo $files)
will work.
Or, if you wish to make it clear that you're doing this to split it, you can use string split like
set files (string split \n -- $files)
which will then end up the same (because currently string split only adds newlines), but looks a bit clearer. (The "--" is the option separator, so nothing in $files is interpreted as an option)
The latter requires fish >= 2.3.0.

Call script on all file names starting with string in folder bash

I have a set of files I want to perform an action on in a folder that i'm hoping to write a scipt for. Each file starts with mazeFilex where x can vary from any number , is there a quick and easy way to perform an action on each file? e.g. I will be doing
cat mazeFile0.txt | ./maze_ppm 5 | convert - maze0.jpg
how can I select each file knowing the file will always start with mazeFile?
for fname in mazeFile*
do
base=${fname%.txt}
base=${base#mazeFile}
./maze_ppm 5 <"$fname" | convert - "maze${base}.jpg"
done
Notes
for fname in mazeFile*; do
This codes starts the loop. Written this way, it is safe for all filenames, whether they have spaces, tabs or whatever in their names.
base=${fname%.txt}; base=${base#mazeFile}
This removes the mazeFile prefix and .txt suffix to just leave the base name that we will use for the output file.
./maze_ppm 5 <"$fname" | convert - "maze${base}.jpg"
The output filename is constructed using base. Note also that cat was unnecessary and has been removed here.
for i in mazeFile*.txt ; do ./maze_ppm 5 <$i | convert - `basename maze${i:8} .txt`.jpg ; done
You can use a for loop to run through all the filenames.
#!/bin/bash
for fn in mazeFile*; do
echo "the next file is $fn"
# do something with file $fn
done
See answer here as well: Bash foreach loop
I see you want a backreference to the number in the mazeFile. Thus I recommend John1024's answer.
Edit: removes the unnecessary ls command, per #guido 's comment.

Search folder for files with matching prefix and perform action in applescript

I have 10s of thousands of PDF files and I'd like to find ones that match certain characteristics and perform an action (merge into a PDF)
For example, I have files like:
filegroup1_abc.pdf
filegroup2_xyz.pdf
filegroup3_qrs.pdf
filegroup3_lmn.pdf
I'd like to find every file that has the same prefix, "filegroup1" and merge them into one PDF, then find the next match (filegroup2), then the next, etc...
So in my above example the last two would be merged as a new PDF since they have "Filegroup3" prefix.
I have found a script to merge PDFs using applescript; so my main question is how to search through the folder, find those files, then perform an action on them. However the pattern of "filetype1" is not known, so the script would first need to check every file and compare when the first X number of characters match in the filename. In this case it's the first 22 characters are identical and indicate a relationship between the files.
Also, I'm trying to do this in applescript but it might be easier to do with another method.
--choose working folder
set ff to quoted form of POSIX path of (choose folder)
try
--returns files matching names as return-delimited text, filter to list of "paragraphs"
-- using built-in AS object text awareness
[EDIT]
--OLD; incorrect:
--set allABCs to every paragraph of (do shell script "cd " & ff & ";" & "ls filegroup*_qrs.pdf")
-- new and improved:
set allABCs to every paragraph of (do shell script "cd " & ff & ";" & "ls filegroup1_???.pdf")
--that matches only 3-character strings. The following
-- matches any number of characters between _ and .pdf:
--set allABCs to every paragraph of (do shell script "cd " & ff & ";" & "ls filegroup1_*.pdf")
on error--it's in a try because if you mistakedly look for files that aren't there,
-- it will return an error. As an aside, doing 'ls' and getting folders
-- returns extra empty strings -- just a cautionary note that probably doesn't matter here
return {}
end try
--and one more caution which you may not need -- you may need to sort results to order the files correctly, if that matters to you

Rename files based on symbols in the name

I have a lot of files (images) like this (file names consist only from numbers):
123456.jpg
369258.jpg
987123.jpg
...
I need to make a copy of each in some other folder (let's name it output) and rename each of the file based on numbers in their name, something like this (in pseudocode):
outputFileName = String(filename[0]) + String(filename[1]) + String(filename[2]+filename[3]) + ".jpg"
So as you can see, the renaming involves getting a certain symbol in file name and sometimes getting a sum of some symbols in file name.
I need to make a script to mass rename all *.jpg in the folder where I put the script based on similar algorithm, and output renamed ones in output folder I mentioned earlier.
This script should be workable from macos terminal and windows via cygwin shell.
I assume main problems are: how to get particular character of bash variable and how to perform addition in bash.
To obtain a char from bash variable you can use this form: ${var:START_INDEX:LENGTH}.
To perform addition: $((ARG1 + ARG2))
Your resulting script may be like that:
#!/bin/bash
for f in *.jpg
do
output=${f:0:1}${f:1:1}$((${f:2:1} + ${f:3:1})).jpg
mv -- "$f" "$output"
done
You are looking for substring extraction.
The syntax is ${string:position:length}, where string is the name of the variable, position is the starting position (0 is the first index), and length is the length of the substring.
A script that would create the filenames as specified in the question, and copy them for a folder named "input" to a folder named "output" could look like this:
#!/bin/bash
for file in input/*.jpg
do
filename="$(basename "$file")"
firstChar="${filename:0:1}"
secondChar="${filename:1:1}"
thirdAndFourthChar="$(( ${filename:2:1} + ${filename:3:1} ))"
newfilename="$firstChar$secondChar$thirdAndFourthChar.jpg"
cp "$file" "output/$newfilename"
done

Resources