How to modify file names in bash - bash

I have a directory like: /data/work/files/
calldata_phonecalls_2131201401_01.zip
calldata_phonecalls_7373201401_02.zip
In this directory I want to create new files corresponding to the zipfiles like , but modifying a small part of the name in BASH:
calldata_calllog_2131201401_01.tsv
calldata_calllog_7373201401_02.tsv
pl note "phonecalls" changed to "calllog"
Pl help.

Using Shell Parameter Expansion and basename:
for f in /data/work/files/*.zip; do
mv "$f" "$(basename "${f/phonecalls/calllog}" .zip).tsv"
done
Using rename (part of perl distribution):
rename 's/phonecalls/calllog/;s/\.zip$/.tsv/' /data/work/files/*.zip

Using rename :
Using one version of rename:
rename 's/^fgh/jkl/' fgh*
Using another version of rename :
rename fgh jkl fgh*
You should check your platform's man page to see which of the above applies.
Using mv:
find ./ -name "*.xyz\[*\]" | while read line
do
mv "$line" ${line%.*}.xyz
done
Another way
ls -1 | nawk '/foo-bar-/{old=$0;gsub(/-\(.*\)/,"",$0);system("mv \""old"\" "$0)}'
Yet another:
for f in fgh*; do mv $f $(echo $f | sed 's/^fgh/jkl/g'); done
There are several other answers under stackoverflow and superuser pages:
Copied/summarized from the following links:
Rename multiple files in Unix
How to use mv command to rename multiple files in unix?

Related

Bash: move and rename files to parent directory

I have the following working code:
for f in */*/*.jpg; do rename 's#/picture#p#' "$f"; done &&
for f in */*.jpg; do rename 's#/subfolder#s#' "$f"; done
However, this does not work with the following verison of rename: https://linux.die.net/man/1/rename
The following folder structure:
folder1/subfolder1/picture1.png
folder1/subfolder2/picture1.png
folder1/subfolder2/picture2.png
folder2/subfolder1/picture1.png
should be converted into the following structure
f1s1p1.png
f1s2p1.png
f1s2p2.png
f2s1p1.png
Check out the man page for rename that you linked yourself: the command takes two strings "from" and "to" (plus a list of files).
Synopsis
rename from to file...
You could just try the following:
for f in */*.jpg */*/*.jpg; do mv "$f" "$(echo $f | sed 's/\([a-zA-Z]\)[^0-9]*\([0-9][0-9]*\)/\1\2/g' | tr -d /)"; done
This will, however, leave the original folders in place which you will then have to remove manually.

Shell script: Check if a Directory is of YYYY_MM_DD_HH this format

I have a script that creates a file list of directories available in another path.
Now, I would like to do some tasks only if the Directory is of the format "YYYY_MM_DD_HH" in this file list.
My file list has following entries:
2014_04_21_01
asdf
2012_01_19_10
2010_01
Now I would like to move the directories with names as YYYY_MM_DD_HH to another path. I.e., only 2014_04_21_01 & 2012_01_19_10 MUST be MOVED.
Please advise.
Use bash regex pattern matching:
for dir in $list
do if [[ "$dir" =~ ^[0-9]{4}_[0-9]{2}_[0-9]{2}_[0-9]{2}$ ]]
then mv "$dir" newdir/
fi
done
Assuming you have a GNU version of sed on your computer, you could use it to easily parse your directory names and execute a command.
Say we have following input file:
2014_04_21_01
asdf
2012_01_19_10
2010_01
2012_01_19_10_09
62012_01_19_10
You can search for your regex with sed and replace it with a mv command as follows:
sed 's/^[0-9]\{4\}\(_[0-9]\{2\}\)\{3\}$/mv "&" "other_dir"/' file_list
will output:
mv "2014_04_21_01" "other_dir" # We want to run this
asdf
mv "2012_01_19_10" "other_dir" # and this
2010_01
2012_01_19_10_09
62012_01_19_10
Now if you add the (GNU sed) e option at the end of sed substitution (and -n option before sed script to ensure only successul substitutions are executed), the generated command will be piped into your shell:
sed -n 's/^[0-9]\{4\}\(_[0-9]\{2\}\)\{3\}$/mv "&" "other_dir"/e' file_list
# ^^ ^
I would recommand to run it first without the e option so as to check that mv commands will be properly formatted.
Why to make separate file for file list. Just go in that directory execute following command. I have taken the destination directory as /home/newdir/
ls | grep [0-9][0-9][0-9][0-9]_[01][0-9]_[0123][0-9]_[012][0-9] | awk '{print $0" /home/newdir/"}' | xargs mv
Be Careful while working with dates. As you have mentioned that file name is in format YYYY_MM_DD_HH then we have restrictions on MM,DD and HH. If we talk about restrictions then we know how a calendar is constructed. So 9999_99_99_99 is invalid file name. It is not satisfying YYYY_MM_DD_HH.
We have to build script for restrictions or I can say whole calendar. Still working on it.
Example:
perl -nle 'system("mv $_ dir/year$1") if /^(\d{4})_\d\d_\d\d_\d\d/$' flist
would extract the year and rename dir 2014_04_21_01 to dir/year2014
This single find command with -regex option should take care of this:
cd /base/path/of/these/dirs
find . -type d -regextype posix-egrep -regex '.*/[0-9]{4}_[0-9]{2}_[0-9]{2}_[0-9]{2}$' \
-exec mv '{}' /dest/dir/ \;

How to rename some file of same pattern in shell scripting

I want to write a code is shell scripting which will rename all the files of extension .txt in a current directory to extension .c .Suppose my current directory contains some 100 .txt file. This number is not fixed.
for f in *.txt; do echo mv "$f" "${f%.txt}.c"; done
Remove "echo" when you're satisfied it's working. See the bash manual for the meaning of "%" here.
See man rename. You can rename multiple files providing regexp substitution.
rename 's/\.txt$/.c/' *.txt
If you don't have rename in you system, you can use find:
find . -name '*.txt' | while read FILE; do echo mv "$FILE" "$(echo "$FILE" | sed 's/\.txt$/.c/g')"; done
Remove echo when you verify it does what you want.
awk can do this trick too:
kent$ ls *.txt|awk '{o=$0;gsub(/txt$/,"c"); print "mv "o" "$0;}'|sh

Rename all files in directory from $filename_h to $filename_half?

Dead simple.
How do I rename
05_h.png
06_h.png
to
05_half.png
06_half.png
At least, I think it's simple, but it's hard to Google for this kind of thing unless you already know.
Thanks....
Just use bash, no need to call external commands.
for file in *_h.png
do
mv "$file" "${file/_h.png/_half.png}"
done
Do not add #!/bin/sh
For those that need that one-liner:
for file in *.png; do mv "$file" "${file/_h.png/_half.png}"; done
Try rename command:
rename 's/_h.png/_half.png/' *.png
Update:
example usage:
create some content
$ mkdir /tmp/foo
$ cd /tmp/foo
$ touch one_h.png two_h.png three_h.png
$ ls
one_h.png three_h.png two_h.png
test solution:
$ rename 's/_h.png/_half.png/' *.png
$ ls
one_half.png three_half.png two_half.png
for f in *.png; do
fnew=`echo $f | sed 's/_h.png/_half.png/'`
mv $f $fnew
done
Or in one-liner:
for f in *.png; do mv "$f" "$(echo $f | sed 's/_h.png$/_half.png/g')"; done
Are you looking for a pure bash solution? There are many approaches, but here's one.
for file in *_h.png ; do mv "$file" "${file%%_h.png}_half.png" ; done
This presumes that the only files in the current directory that end in _h.png are the ones you want to rename.
Much more specifically
for file in 0{5..6}_h.png ; do mv "$file" "${file/_h./_half.}" ; done
Presuming those two examples are your only. files.
For the general case, file renaming in has
been covered
before.
Use the rename utility written in perl.
Might be that it is not available by default though...
$ touch 0{5..6}_h.png
$ ls
05_h.png 06_h.png
$ rename 's/h/half/' *.png
$ ls
05_half.png 06_half.png
for i in *_h.png ; do
mv $i `echo "$i"|awk -F'.' '{print $1"alf."$2}'`
done
I had a similar question:
In the manual, it describes rename as
rename [option] expression replacement file
so you can use it in this way
rename _h _half *.png
In the code:
'_h' is the expression that you are looking for.
'_half' is the pattern that you want to replace with.
'*.png' is the range of files that you are looking for your possible target files.
Hope this can help c:
Another approach can be manually using batch rename option
Right click on the file -> File Custom Commands -> Batch Rename
and you can replace h. with half.
This will work for linux based gui using WinSCP etc
One liner:
for file in *.php ; do mv "$file" "_$file" ; done
Although the answer set is complete, I need to add another missing one.
for i in *_h.png;
do name=`echo "$i" | cut -d'_' -f1`
echo "Executing of name $name"
mv "$i" "${name}_half.png"
done
I had to rename the prefix of files and I found this answer with a solution like this:
for i in h_*; do mv ${i/#h_/half_}; done
If pattern begins with #, it must match at the beginning of the
expanded value of parameter. If pattern begins with %, it must match
at the end of the expanded value of parameter.
from man bash
Use the rename utility:
rc#bvm3:/tmp/foo $ touch 05_h.png 06_h.png
rc#bvm3:/tmp/foo $ rename 's/_h/_half/' *
rc#bvm3:/tmp/foo $ ls -l
total 0
-rw-r--r-- 1 rc rc 0 2011-09-17 00:15 05_half.png
-rw-r--r-- 1 rc rc 0 2011-09-17 00:15 06_half.png

How to rename files on a date base in the shell?

I'd like to rename some files that are all in the same directory. The file name pattern used is Prefix_ddmmyy.tex with a european date format. For the sake of readability and the ordering I'd like to rename the files in a pattern Prefix_yymmdd.tex with a canonical date format.
Anyone ideas how I can do this automatically for a complete directory? My sed and regexp knowledge is not very sharp...
for file in Prefix_*.tex ; do
file_new=echo "$file" | sed -e 's:\([0-9][0-9]\)\([0-9][0-9]\)\([0-9][0-9]\)\(\.tex\):\3\2\1\4:'
test "$file" != "$file_new" && mv -f "$file" "$file_new"
done
Or, if you have a lot of files and/or want to process files recursively, replace:
for file in Prefix_*.tex ; do
with:
find . -name Prefix_*.tex -print | while read file ; do
or (non-recursive, GNU):
find . -maxtdepth 1 -name Prefix_*.tex -print | while read file ; do
You can also do it with any bourne-type shell without external commands:
for f in *.tex; do
_s=.${f##*.} _f=${f%.*} _p=${f%_*}_
_dt=${_f#$_p} _d=${_dt%????} _m=${_dt%??}
_y=${_dt#$_m} _m=${_m#??}
mv -- "$f" "$_p$_y$_m$_d$_s"
done
With zsh it would be:
autoload -U zmv
zmv '(*_)(??)(??)(??)(.tex)' '$1$4$3$2$5'
You can try "mmv".

Resources