Change extension of file using shell script - linux

How to change extension of all *.dat files in a directory to *.txt.
Shell script should take the directory name as an argument. Can
take multiple directories as arguments. Print the log of command
result in appending mode with date and timestamp.

Bash can do all of the heavy lifting such as extracting the extension and tagging on a new one. For example:
for file in $1/*.dat ; do mv "$file" "${file%.*}.txt" ; done

Batch File Rename By File Extension in Unix
# change .htm files to .html
for file in *.htm ; do mv $file `echo $file | sed 's/\(.*\.\)htm/\1html/'` ; done
# change .html files to .htm
for file in *.html ; do mv $file `echo $file | sed 's/\(.*\.\)html/\1htm/'` ; done
#change .html files to .shtml
for file in *.html ; do mv $file `echo $file | sed 's/\(.*\.\)html/\1shtml/'` ; done
#change .html files to php
for file in *.html ; do mv $file `echo $file | sed 's/\(.*\.\)html/\1php/'` ; done
so ==>
# change .dat files to .txt
for file in *.dat ; do mv $file `echo $file | sed 's/\(.*\.\)dat /\1txt/'` ; done

#!/bin/bash
for d in $*; do
for f in $(ls $d/*.dat); do
echo $(date) $(mv -v $f ${f%.dat}.txt)
done
done
Output redirection should be done by the shell when running the script
Leaving out argument validity checks

Simple script:
#!/bin/bash
if [ $# -lt 2 ] then
echo "Usage `basename $0` <any number of directories space separated>"
exit 85 # exit status for wrong number of arguments.
fi
for directories
do
for files in $(ls $directories/*.dat); do
echo $(date) $(mv -v $files ${files%.dat}.txt)
done
done
The first for loop by default loops on the $# i.e. command-line arguments passed.

Follow Pben's solution, if your filename contains blank space, you should use double quotation marks to the variable like the following:
#remove the space in file name
#example file name:19-014-0100.mp3 .mp3
#result file name:19-014-0100.mp3
$ for file in *.mp3 ;
do target=`echo "$file" | sed 's/ //g'`;
echo "$target";
mv "$file" "$target";
done;
#remove the duplicate file extension in file name
#example file name:19-014-0100.mp3.mp3
#result file name:19-014-0100.mp3
$ for file in *.mp3 ;
do target=`echo "$file" | sed 's/\.mp3\.mp3$/.mp3/g'`;
echo "$target";
mv "$file" "$target";
done;

To rename (changing extention) all my html files on epub files I use this command line :
find . -name "*.html*" -exec rename -v 's/\.html$/\.epub/i' {} \;

Script, first finds the names of the given extensions.
It removes the extension from names. Then adds backslash()
for identification of terminal.
Then the 'mv' command executed.
Here the '.temp' folder is used to hide the process from user,
in GUI.
#!/bin/sh
if [ $# -ne 3 ]
then
echo "Usage: ./script folder current_extension modify_extension"
exit
fi
mkdir .temp
find $1 -name "*.$2" > .temp/output_1 && sed "s/$2//" .temp/output_1 > .temp/output_2 && sed -e "s/[ \t]/\\\ /g" .temp/output_2 > .temp/output_3
while read line
do
mv -v "$line""$2" "$line""$3"
done < .temp/output_3
rm -rf .temp
The output files are saved inside the '.temp' folder,later the '.temp' folder is removed.

The top voted answer didn't really work for me. I may have been doing something wrong. My scenario was trying to create a file with the original name, but with the date appended to it, along with changing the extension from .xslx to .csv. This is what worked for me:
csvname=`echo $xlsx |sed 's/\.xlsx//'`"-$now"`echo $xlsx | sed 's/\(.*\.\)xlsx/\.csv/'`
So, for all the .dat files in a directory (without the date addition), you could run something like this:
for i in *.dat
do mv $i `echo $i |sed 's/\.dat//'``echo $i | sed 's/\(.*\.\)dat/\.txt/'`
done
From the above, this section of code just removed the extension:
echo $i |sed 's/\.dat//'
And this section changes the .dat to .txt:
echo $i | sed 's/\(.*\.\)dat/\.txt/'
And by bumping them next to each other, it concatenates the two outputs into the filename. It's like doing this:
mv [filename][.dat] [filename] + [.txt]
Though, I did use STDOUT instead of the 'mv' command.

Following command to change file extention .c to .h
find . -depth -name "*.c" -exec sh -c 'dname=$(dirname {}) && fname=$(basename {} .c) && mv {} $dname/$fname.h' ";"

change js to cjs extension files recursively:
cd dist # where you place your .js
for file in $(find . -type f -name "*.js"); do mv "$file" "${file%.*}.cjs"; done

Related

Recursively get all the files and dirs they are located in

Trying to run a script that will fetch all directories, and files containing these directories, and logs data onto a .CSV file.
So, if I were to have structure like:
mainDir.dir -> [sub1.dir -> file01.png, sub2.dir -> file02.png]
, I would get a CSV of
dir; file
sub1; file01.png
sub2; file02.png
This is the script I currently have
for dir in */ .*/ ;
do
for entry in $dir
do
path="$entry"
empty=""
file="${$dir/$empty}"
echo -e "$dir;$file;" >> file.csv
done
done
find is useful for processing many files recursively.
Command
find . -type f -execdir sh -c "pwd | tr -d '\n' >> ~/my_file.csv; echo -n ';' >> ~/my_file.csv; echo {} | sed -e 's/^\.\///' >> ~/my_file.csv" \;
Note: make sure you do not give a relative path to the output CSV file. execdir changes the working directory (and that is what makes pwd work).
Breakdown
find . -type f find all files recursively starting here
-execdir sh -c "pwd | tr -d '\n' >> ~/my_file.csv; echo -n ';' >> ~/my_file.csv; For each file, execute in its directory pwd. Strip the newline and add directory name to output. Also add a semicolon, again with no newline.
echo {} | sed -e 's/^\.\///' >> ~/my_file.csv" \; Append filename to output. This time, leave newline, but by default find will place the ./ in front of the filename. The sed here removes it.
If you don't need more than one level deep, this seems to work
for i in **/*; do echo $i | tr / \; ; done >> file.csv

getting filenames from directory in shell script

I would like to iterate a loop over all the file present in a directory using shell script. Further, I would like to display the contents from each file. I am passing directory as a command line argument.
I have a simple loop as follows:
for file in $1
do
cat $file
done
If I run
sh script.sh test
where test is a directory, I get content of first file only.
Could anyone please help me in this?
couple of alternatives:
compact modification of SMA's code:
for file in $1/*
do
[[ -f $file ]] && cat $file
done
or use find:
find $1 -type f -exec cat \{\} \;
Try something like:
for file in $1/*
do
if [[ -f $file ]] ##you could add -r to check if you have read permission for file or not
then
cat $file
fi
done

After extracting files with 7zip how can you rename those file and save

I am using the following command to extract the files with 7zip
7za x -p$passwd $file -o$outdir
there are many files getting extracted, i want to rename these files after extraction how can i do it with help of writing a script in ksh
files=`ls ABC_0722*.zip | xargs -r`
outdir="/abc/def/prq/xyz"
for file in $files; do
passwd=`echo $file| awk '{print substr($0,11,2)}'``echo ABC``echo $file| awk '{print substr($0,5,2)}'`
7za x -p$passwd $file -o$outdir
done
After the extraction I need to rename the files to abcdef.
After decompress all files, use a for loop looking for all normal files that are not symbolink links, replace the basename of the path and replace it with abcde plus a counter:
for f in $outdir/*; do
[[ -f $f && ! -L $f ]] && { ((++i)); mv -- "$f" "${f%/*}/abcde$i"; };
done

change *.foo to *.bar in unix one-liner

I am trying to convert all files in a given directory with suffix ".foo" to files containing the same basename but with suffix modified to ".bar". I am able to do this with a shell script and a for loop, but I want to write a one-liner that will achieve the same goal.
Objective:
Input: *.foo
Output: *.bar
This is what I have tried:
find . -name "*.foo" | xargs -I {} mv {} `basename {} ".foo"`.bar
This is close but incorrect. Results:
Input: *.foo
Output: *.foo.bar
Any ideas on why the given suffix is not being recognized by basename? The quotes around ".foo" are dispensable and the results are the same if they are omitted.
Although basename can work on file extensions, using the shell parameter expansion features is easier:
for file in *.foo; do mv "$file" "${file%.foo}.bar"; done
Your code with basename doesn't work because the basename is only run once, and then xargs just sees {}.bar each time.
for file in *.foo ; do mv $file echo $file | sed 's/\(.*\.\)foo/\1bar/' ; done
Example:
$ ls
1.foo 2.foo
$ for file in *.foo ; do mv $file `echo $file | sed 's/\(.*\.\)foo/\1bar/'` ; done
$ ls
1.bar 2.bar
$
for x in $(find . -name "*.foo"); do mv $x ${x%%foo}bar; done
$ for f in *.foo; do echo mv $f ${f%foo}bar; done
mv a.foo a.bar
mv b.foo b.bar
Remove echo when ready.
If you have installed mmv, you can do
mmv \*.foo \#1.bar
.
Why don't you use "rename" instead of scripts or loops.
RHEL: rename foo bar .*foo
Debian: rename 's/foo/bar/' *.foo

Add file extension to files with bash

What is the good way to add file extension ".jpg" to extension-less files with bash?
# Strip .jpg from all filenames
for f in *.jpg; do mv "$f" "${f%.jpg}"; done
# Add .jpg to all filenames (even those with .jpg already)
for f in *; do mv "$f" "$f.jpg"; done
# Add .jpg to all filenames...unless they are already .jpg
for f in *; do case "$f" in *.jpg) echo skipped $f;; *) mv "$f" "$f".jpg; esac; done
# Add .jpg to all filenames...unless they already have a . extension
for f in *; do case "$f" in *.*) echo skipped $f;; *) mv "$f" "$f".jpg; esac; done
You can use rename:
rename 's/(.*)/$1.jpg/' *
Another way - without loops
find . -type f -not -name "*.*" -print0 |\
xargs -0 file |\
grep 'JPEG image data' |\
sed 's/:.*//' |\
xargs -I % echo mv % %.jpg
Breakdown:
find all files without extension
check the file type
filter out only JPG files
delete filetype info
xargs run the "mv" for each file
the above command is for dry run, after it you should remove the "echo" before mv
EDIT
Some people suggesting that here is needed "Wrap path arguments in quotes; avoids argument splitting on paths with spaces".
Usually, this recommendation is true, in this case isn't. Because, here the % is got replaced not by shell expansion but by the xargs internally (directly), so the % will be substituted correctly even with spaces in filenames.
Simple demo:
$ mkdir xargstest
$ cd xargstest
# create two files with spaces in names
$ touch 'a b' 'c d'
$ find . -type f -print
./c d
./a b
# notice, here are spaces in the above paths
#the actual xargs mv WITHOUT quotes
$ find . -type f -print | xargs -I % mv % %.ext
$ find . -type f -print
./a b.ext
./c d.ext
# the result is correct even in case with spaces in the filenames...
Simple,
cd to the directory where your files are and:
for f in *;do mv $f $f.jpg;done
dry run:
rename -n s/$/.jpg/ *
actual renaming:
rename s/$/.jpg/ *
find . | while read FILE; do if [ $(file --mime-type -b "$FILE") == "image/jpeg" ]; then mv "$FILE" "$FILE".jpg; fi; done;
In my case i was not aware of the filetype so i used the mv command with the help of the file command to examine and possibly find the file type. This solution might not be perfect for all files since the file command might not recognize the filetype but it worked mostly good for me.
for f in *; do ext=$(file $f | awk '{print $2;}'); mv -n "$f" "$f.$ext"; done
The use of awk is to strip the second word of the string returned from the command file that is actually the extension.
rename --dry-run * -a ".jpg" # test
* -a ".jpg" # rename
You can use move multiple files. I am a maintainer of this project. The syntax is simple.
mmf files*
It will open your $EDITOR with all files names, or vim by default and you can simply highlight the end of all file names using Ctrl+v+G in vim , save the file,quit and that it , all your files are renamed
Ryan Li
The correct syntax for adding a file extension to multiple files within a directory which do not have a file extension is
find . | while read FILE; do if [[ -n `file --mime-type "$FILE" | grep 'message/rfc822'` ]]; then mv "$FILE" "$FILE".eml; fi; done;

Resources