Appending date to filename, but before file extension [duplicate] - bash

This question already has answers here:
Appending a string before file extension in Bash script
(3 answers)
Append date to filename in linux
(7 answers)
Closed 27 days ago.
I need to append the current date to the end of a filename and currently use the following:
mv "$file" "${file}_$dateNow"
So if I have the file Testfile1.xml it will get changed to Testfile1.xml230124
What I want to do is place the date after the filename, but before the file extension, ie Testfile1120324.xml
How may I do this? The file extension will not always be .xml, could be anything of any length.
Many thanks for any help

This solution works using bash's parmeter expansion / pattern matching and and will place the date before multiple file extensions such as tar.gz:
mv "${file}" "${file%%.*}${dateNow}.${file#*.}"
# myfile.tar.gz -> myfile2023_01_24.tar.gz
The first part of this solution uses ${file%%.*} to greedily match against the .* at the end of the variable and cuts out that match returning the difference.
The end part, ${file#*.}, works in the opposite way by lazily matching against the *. pattern at the beggining of the variable and returning the difference.
If for example you want to place the date before just the first extension you can do this:
mv "${file}" "${file%.*}${dateNow}.${file##*.}"
# google.com.txt -> google.com2023_01_24.txt
I am assuming you have properly set the $dateNow var, however if you haven't then you can use the date command to insert the date:
mv "${file}" "${file%%.*}$(date +'%Y_%m_%d').${file#*.}"

filename="example.txt"
extension="${filename##*.}"
filename="${filename%.*}"
new_filename="$filename-$(date +%Y%m%d).$extension"
mv "$filename.$extension" "$new_filename"
This command first saves the file extension to a variable called "extension" by using the parameter substitution pattern ${filename##.}.
Then it saves the filename without the extension to a variable called "filename" by using the parameter substitution pattern ${filename%.}.
Then it creates a new variable called "new_filename" that appends the current date to the filename before the extension by using the command date +%Y%m%d and concatenating it to the filename variable.
Finally, it uses the mv command to rename the original file to the new filename.
This command will append the current date formatted as YYYYMMDD to the file name before the extension, you can change the format of the date by changing the parameter on `date

I would first put $file apart into everything before and after the extension:
if [[ $file =~ ^(.*)[.]([^./]*)$ ]]
then
newname=${BASH_REMATCH[1]}_$dateNow.${BASH_REMATCH[2]}
else
newname=$file_$dateNow
fi
mv -n "$file" "$newname"
Note that this also handles cases such as when file contains a.b/c.d (extension is just d, not b/c.d) and when file contains ' a.b/c` (no extension provided).
The -n catches the case that we accidentally already have a file named $newname in our directory.

Related

Globbing a filename and then saving to a variable

I've got some files in a directory with a standard format, I'm looking to use a txt file with part of the filenames to extend them through * then finally add on a .gz tag as an output
For example, a file called 1.SNV.111-T.vcf in my directory, I have 111-T in my txt file.
#!/bin/bash
while getopts f: flag
do
case "${flag}" in
f) file=${OPTARG};;
esac
done
while IFS="" read -r p || [ -n "$p" ]
do
vcf="*${p}.vcf"
bgzip -c ${vcf} > ${vcf}.gz
done < $file
This will successfully run bgzip but actually save the output to be:
'*111-T.vcf.gz'
So adding .gz at the end has "deactivated" the * character, as pointed out by Barmar this is because there isn't a file in my directory called 1.SNV.111-T.vcf.gz so the wildcard is inactivated, please can anyone help?
I'm new to bash scripting but I assume there must be some way to save the "absolute" value of my vcf variable so that once it has found a match the first time, it's now a string that can be used downstream? I really cant find anything online.
The problem is that wildcards are only expanded when they match an existing file. You can't use a wildcard in the filename you're trying to create.
You need to get the expanded filename into the vcf variable. You can do it this way:
vcf=$(echo *"$p.vcf")
bgzip -c "$vcf" > "$vcf.gz"

Need to move a file having double digit numeric values in bash script

I want to move few files that have numeric values as part of filename. For example pattern 'ABC123_10_abc.txt' to a destination location as 'ABC123_abc.txt '
current command used:
mv $prefix_[0-9]_$suffix $prefix_$suffix;
But the above is not working for double digits. whereas if i put *[0-9] it is creating issue in identifying unique filename, so please suggest
Your question is tagged as bash, so I assume scripting is ok. This should help:
#!/usr/bin/env bash
for file; do
if [[ "${file}" =~ ^(.+)_[0-9]+_(.+)$ ]]; then
prefix="${BASH_REMATCH[1]}"
suffix="${BASH_REMATCH[2]}"
mv -v "${file}" "${prefix}_${suffix}"
else
echo "File '${file}' does not match regex, skipping"
fi
done
Save this script as e.g. renamer.sh, make it executable and run it with the files to rename provided as arguments, e.g. ./renamer.sh *.txt.
Each filename is matched against regular expression ^(.+)_[0-9]+_(.+)$ which dissects the filename into prefix and suffix per your description. The file is then renamed to ${prefix}_${suffix}. Works for any number of digits regarding the _xx_ part.
Note that this is based on the little information you provided. If that's not what you're looking for, please clarify your question by adding further details.

Rename files to drop a date stamp

I have files in an input directory. The file names are as given below
SEMAPHOREINPUT_10-06-2015.xlsx
WRAPPERINPUT_10-06-2015.xlsx
These files will be updated on daily basis so, tomorrow, the input would be
SEMAPHOREINPUT_11-06-2015.xlsx
WRAPPERINPUT_11-06-2015.xlsx
I need to rename these files to the filenames below:
SEMAPHOREINPUT.xlsx
WRAPPERINPUT.xlsx
I tried using the shell script below, but it is not working.
#!/bin/bash
ls | while read FILES
do
newfile = ${FILES/\SEMAPHOREINPUT_.*.xlsx/}
mv $newfile /home/test
done
Three critical errors:
When assigning a value to a variable, there must not be spaces before and after the = sign.
Your string substitution is wrong. To drop the date stamp portion of the filename, use
${FILES/_[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]/}
… or better yet, since the pattern to be dropped must occur at the end of the string,
${FILES%_[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9].xlsx}.xlsx
You seem to have confused the initial and final filenames.
There are other problems as well:
Parsing the output of ls is more complex and less reliable than a loop with a glob:
for file in *; do
…
done
Better yet, be explicit to avoid surprises:
for file in SEMAPHOREINPUT_[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9].xlsx
WRAPPERINPUT_[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9].xlsx; do
mv "$file" "/home/test/${file%_[0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9].xlsx}.xlsx"
done
You can do it like this
for i in `ls *.xlsx`;
do n1=`echo $i|cut -d '_' -f1`;
t=`echo $n1.xlsx`;
mv $i $t;
mv $t /home/test;
done

Using a variable as a file name and writing to the file in bash

So I am trying to write to a file and I want my file name to be the time when I opened it. I have something like this:
var=$(date +"%D-%H:%M:%S")
echo "I opened a file" > $var
cat $var
When I try to run this code, it gives me the error:
NO SUCH FILE OR DIRECTORY
Can someone tell me what is going on?
You should use quotes in your variable while using it:
var="$(date +"%d-%m-%Y-%H:%M:%S")"
echo "I opened a file" > "$var"
cat "$var"
EDIT: Your problem is use of slash / in your date variable that makes shell thinks that it is a path rather than a file name. You will need to change your date format as I suggested above.
That's because your redirect is to the file name ':' and the rest of the filename are being wordsplit into following parameters. Use more quotes.
The other problem is that you cannot create a filename with slashes in it. Of course, that's a directory structure. You will need to replace the slashes with something else. You can do this by modifying the date command, but it's also easy to use parameter expansion, as I show below:
var=$(date +"%D-%H:%M:%S")
$ echo "$var"
03/12/14-15:20:46
$ echo 'data' > "${var//\//_}" # replace slashes with underscores
If you're just trying to create datestamped files, consider using the epoch timestamp:
var=$(date +%s)
echo "$somedata" > "$var"
It's easy for a machine to convert values to/from the epoch timestamp, and it's easier for you and most users to deal with a filename made up of a string of digits than a human-readable timestamp.

Do not start loop if there is no files in directory?

All,
I am running BASH in Solaris 10
I have the following shell script that loops in a directory depending on the presence of CSV files.
The problem is with this piece of code is that it still does one loop even if there is no CSV files in that directory and then calls SQL loader.
SQLLoader then produces a log file because there is no file to process and this is beginning to mess up my directory filling it with log files.
for file in *.csv ;
do
echo "SQLLoader is reading : " $file
sqlldr <User>/<Password>#<DBURL>:<PORT>/<SID> control=sqlloader.ctl log=$inbox/$file.log data=$inbox/$file
done
How do I stop it going into a loop if there is no CSV files in that directory of $inbox
Say:
shopt -s nullglob
before your for loop.
This is not the default, and saying for file in *.csv when you don't have any matching files expands it to *.csv.
Quoting from the documentation:
nullglob
If set, Bash allows filename patterns which match no files to expand to a null
string, rather than themselves.
Use find to search files
for file in `find -name "*.csv"` ;
First off, using nullglob is the correct answer if it is available. However, a POSIX-compliant option is available.
The pattern will be treated as literal text if there are no matches. You can catch this with a small hack:
for file in *.csv; do
[ -f "$file" ] || break
...
done
When there are no matches, file will be set to the literal string *.csv, which is not the name of a file, so -f "$file" will fail. Otherwise, file will be set in turn to the name of each file matching the pattern, and -f "$file" will succeed every time. Note this will work even if there is an file named *.csv. The drawback is that you have to make a redundant test for each existing file.

Resources