Rename files with regex incrementing when the same output filename occurs - bash

I have some files:
tridiag_6_a.txt
tridiag_6_b.txt
tridiag_6_c.txt
gauss_6_a.txt
gauss_6_b.txt
and I want to get:
tridiag1.txt
tridiag2.txt
tridiag3.txt
gauss1.txt
gauss2.txt
How can I do this? (Mac OS terminal) I'm stuck on:
$ rename 's/^(.+?)_.*/$1$N.txt/g' *.txt
But this increments through all renames.

Use a Bash loop:
prev_prefix=""
count=1
for file in *.txt; do
[[ -f "$file" ]] || continue
prefix="${file/_*/}" # get all characters up to the first underscore
if [[ "$prev_previx" != "$prefix" ]]; then
count=1
prev_prefix="$prefix"
fi
mv "$file" "$prefix$count".txt
((count++))
done

Related

Filename prefix test always failing in bash

i have a question about using shell to read files. That is to say, i have a folder like this:
folder
new_file.log (this is a file)
new_file2.log (this is a file)
new_file3.log (this is a file)
new (this is a subfolder)
new_file_from_subfolder.log
new_file2_from_subfolder.log
new_file3_from_subfolder.log
what i want is to read all the content from (direct) files, not files from the subfolder. In the above case, i need new_file.log to new_file3.log.
I know there is a simple way:
$ cat new*.log
but i also like to write a bash script:
for file in $(ls -a)
do
if [[ "$file" != "." && "$file" != ".." ]]; then
if [[ -f "$file" && "$file" == "^new" ]]; then **here is the problem**
[do something]
fi
fi
done
my problem is labeled as above. the bash code seems doesnot like
"$file" == ^new
if i run the bash, it basically does nothing, which means that files fail to meet the condition.
anything wrong?
[[ $foo = $bar ]] is a glob expression, not a regex; ^ has no special meaning there.
You probably want either the glob expression
[[ $file = new* ]]
or the regex
[[ $file =~ ^new ]]
Of course, in a real-world scenario, you'd just iterate only over the names that match your prefix:
for file in new*; do
: something with "$file"
done
...or, recursively (using FD 3 so you can still interact with the user in this code):
while IFS= read -u 3 -r -d '' file; do
: something with "$file"
done 3< <(find . -type f -name 'new*' -print0)
You're headed down the wrong track. Here's how to iterate over all regular files starting with new:
for file in new*
do
if [[ -f $file ]]
then
dosomething "$file"
fi
done

How do I manipulate filenames in bash?

I have a bunch of images that I need to rename, so I can use them and I was wondering how to do this.
The way they need to be is that first 5 will be kept and then for the 6th I would write a number from 1-3. I only know that the first 5 are static; on pics belonging to same "family" and can be used for comparison and the 6th char is not known.
Example:
12345random.jpg
12345randomer.jpg
0987654more_random.jpg
09876awesome.jpg
09876awesomer.jpg
09876awesomest.jpg
09876soawesomegalaxiesexplode.jpg
would become.
12345.jpg
123452.jpg
09876.jpg
098761.jpg
098762.jpg
It would be cool if it would only handle the loop so that 3 pics could be only renamed and rest skipped.
I found some stuff on removing letters to certain point, but nothing that use, since I am quite poor at bash scripting.
Here is my approach, but it kind of sucks, since I tried modifying scripts I found, but the idea is there
//I could not figure how to remove the chars after 5th not the other way around
for file in .....*; do echo mv $file `echo $file | cut -c6-`; done
done
//problem also is that once the names conflict it produces only 1 file named 12345.jpg 2nd one will not be created
//do not know how to read file names to array
name=somefile
if [[ -e $name.jpg]] ; then
i=0
while [[ -e $name-$i.jpg]] ; do
let i++
done
name=$name-$i
fi
touch $name.jpg
You can have:
new_file=${file%%[^0-9]*.jpg}.jpg
As a concept you can have this to rename files:
for file in *.jpg; do
[[ $file == [0-9]*[^0-9]*.jpg ]] || continue ## Just a simple check.
new_file=${file%%[^0-9]*.jpg}.jpg
[[ -e $new_file ]] || continue ## Do not overwrite. Delete line if not wanted.
echo "Renaming $file to $new_file." ## Optional message.
mv -- "$file" "$new_file" || echo "Failed to rename $file to $new_file."
done
If you're going to process files that also contain directory names, you'll need some more changes:
for file in /path/to/other/dirs/*.jpg *.jpg; do
base=${file##*/}
[[ $base == [0-9]*[^0-9]*.jpg ]] || continue
if [[ $file == */* ]]; then
new_file=${file%/*}/${base%%[^0-9]*.jpg}.jpg
else
new_file=${file%%[^0-9]*.jpg}.jpg
fi
[[ -e $new_file ]] || continue
echo "Renaming $file to $new_file."
mv -- "$file" "$new_file"
done
you can also try the following code
but be careful all the files should be in .jpg format and pass the name of folder as an argument
#!/bin/bash
a=`ls $1`
for b in $a
do
echo $b
if (( i<4 ))
then
c=`echo $b | cut -c1-5`
let i=i+1
c="$c$i.jpg"
echo $c
else
c=`echo $b | cut -c1-5`
c="$c.jpg"
break
fi
mv $1$b $1$c
done

Error on my code

#!/bin/bash
# When a match is not found, just present nothing.
shopt -s nullglob
files=(*.wav)
if [[ ${#files[#]} -eq 0 ]]; then
echo "No match found."
fi
for file in "${files[#]}"; do
# We get the date part
find_date=$(stat -c %y $file | awk '{print $1}')`
for t in "${parts[#]}"; do
IFS="-." read -ra parts <<< "$file"
if [[ $t == [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ]]; then
file_date=$t
break
fi
done
# If a value was not assigned, then show an error message and continue to the next file.
# Just making sure there is nothing in Array and date before it moves on
if [[ -z $file_date ]]; then
continue
fi
file_year=${file_date:0:4}
file_month=${file_date:6:2}
mkdir -p "$file_year/$file_month"
# -- is just there to not interpret filenames starting with - as options.
echo "Moving: ./"$file "to: " "./"$file_year"/"$file_month
mv "$file" "$file_year/$file_month"
done
I have some files that are .wav.... I want to put the files in an array like I did and then Stat -c %y filename |awk $1 which gives me YYYY-MM-DD and then I wanna put the date in the array so then I can set it 2 variables Year and Month so then I can either make a DIR Year/Month or if DIR is already there then just mv it. which is mkdir -p... Geting errors in my code but I do not think i am reading the file in my array correct.
25: continue: only meaningful in a for',while', or `until' loop
my echo statement Moving: ./OUT117-20092025-5845.wav to: .//
Besides some syntax issues main problem is that you cannot have continue outside for loop.
Syntax error are:
You cannot have space on either side of assignment operator = so find_date= stat -c %y $file | awk{print $1}` should befind_date=$(stat -c %y $file | awk '{print $1}')`
Regex operator is =~ instead of ==
UPDATE: You are starting your for loop before you are setting your variable.
for t in "${parts[#]}"; do
IFS="-." read -ra parts <<< "$file"
It should be:
IFS="-." read -ra parts <<< "$file"
for t in "${parts[#]}"; do

Check if each file in a list (which is in a file) exists in bash

I have a text file (ListOfAllFiles.txt) that has a list of 500 files some of which exist and some don't.
I'd like to make two texts files that indicate which files exist and which don't.
This is my code thus far:
#!/bin/bash
for f in $(cat /path/to/ListOfAllFiles.txt)
do
if [[ -f $f ]]; then
echo $f > /path/to/FilesFound.txt
else
echo $f > /path/to/FilesNOTFound.txt
fi
done
What am I doing wrong??
Your biggest problem is that each pass through the loop will overwrite either /path/to/FilesFound.txt or /path/to/FilesNOTFound.txt; instead of using >, you should be using >>. Fixing that, and making other improvements for robustness, we get:
#!/bin/bash
echo -n > /path/to/FilesFound.txt # reset to empty file
echo -n > /path/to/FilesNOTFound.txt # reset to empty file
while IFS= read -r f ; do
if [[ -f "$f" ]]; then
echo "$f" >> /path/to/FilesFound.txt
else
echo "$f" >> /path/to/FilesNOTFound.txt
fi
done < /path/to/ListOfAllFiles.txt

BASH: comparing strings, one from a file, one in my program, if statement always is true

So I have this block of code. Basically, I'm taking file $i, checking if it's got content or not, checking if I can read it, if I can open it, grab the first line and see if it's a bash file. When I run this every time on a non-empty file, it was registers as true and echo's bash.
## File is empty or not
if [[ -s $i ]]
then
## Can we read the file
if [[ -r $i ]]
then
## File has content
if [[ $(head -n 1 $i) = "#! /bin/bash" ]]
then
echo -n " bash"
fi
fi
else
## file does not have content
echo -n " empty"
fi
This is what does the check of if it's bash:
if [[ $(head -n 1 $i) = "#! /bin/bash" ]]
Replace [[ with [ and enclose $(head -n 1 $i) in quotes.
[[ is itself an operator that tests its contents.

Resources