1

I have multiple files in one directory with below format, so I want to rename all like below:

From

27_07_2017file1vc001vpxd-1605.log
27_07_2017case2vc001vpxd-9169.log
27_07_2017server3vc001vpxd-4640.log
27_07_2017file24vc001vpxd-9170.log
27_07_2017files5vc001vpxd-4641.log

To

file1vc001vpxd-1605_27_07_2017_1.log
case2vc001vpxd-9169_27_07_2017_1.log
server3vc001vpxd-4640_27_07_2017_1.log
file24vc001vpxd-9170_27_07_2017_1.log
files5vc001vp-4641_27_07_2017_1.log

Need to change on format of files from datefilename.log to format filename.date_1.log

Please not to suggest using rename command, since it doesn't work in my linux server.

Thank you

4
  • Have you tried mv 07-2017filename.log filename_31-07-2017_1.log? If you post your script code and a error message we might be able to help you more.
    – Tigger
    Aug 1, 2017 at 4:36
  • 1
    why doesn't rename work? is it not installed? do you only have the primitive rename from util-linux? In either case, install the perl File::Rename version of rename. It can easily do exactly what you want and is the best tool for this job. It's in the rename package on debian-like distros, and many others also have their own package of File::Rename
    – cas
    Aug 1, 2017 at 8:46
  • @cas is right. rename is the right tool for the job. There is no such thing as "It doesn't work". Maybe you need some help installing it, but that's a different issue.
    – r_31415
    Aug 7, 2022 at 0:32
  • You’ve got three different versions of the renaming pattern. Your examples are _<date>_1, you say .<date>_1 in the text of the question, and you say _<date>.1 in a comment. Aug 11, 2022 at 2:17

4 Answers 4

3

Here is a bash script for that (revised for general case based on OP's revised question):

for file in ./*.log; do 
    echo mv "$file" "${file:12:$((${#file}-16))}_${file:2:10}_1.log";
done

Above the ${file:X:Y} is bash substring expansion syntax ${variable:offset:length} and get length of characters start from offset from its variable (or parameter) and rename with mv command. Just remove the echo once you ensure the result is expected as you want.

The result of above is as following:

mv ./27_07_2017case5.log case5_27_07_2017_1.log
mv ./27_07_2017file1vc001vpxd-1605.log file1vc001vpxd-1605_27_07_2017_1.log
mv ./27_07_2017file2vc001vpxd-9169.log file2vc001vpxd-9169_27_07_2017_1.log
mv ./27_07_2017file3vc001vpxd-4640.log file3vc001vpxd-4640_27_07_2017_1.log
mv ./27_07_2017file4vc001vpxd-9170.log file4vc001vpxd-9170_27_07_2017_1.log
mv ./27_07_2017file5.log file5_27_07_2017_1.log
mv ./27_07_2017file5vc001vpxd-4641.log file5vc001vpxd-4641_27_07_2017_1.log
mv ./27_07_2017number-blahblahblah5.log number-blahblahblah5_27_07_2017_1.log
mv ./27_07_2017number-blahblahblah5AAABBC.log number-blahblahblah5AAABBC_27_07_2017_1.log
mv ./27_07_2017number5.log number5_27_07_2017_1.log
0
1

In your case there are two components: filename and date

list all log files with recursive search

find "$(pwd)" -type f -iname "*.log" > loglist

list all log files without recurse to sub-directories

find "$(pwd)" -maxdepth 0 -type f -iname "*.log" > loglist

Now, using while loop you can move files to the desired format

while read f;
do
    # filename only and not complete path
    fname_only=$(basename "$f");
    
    # directory name only without filename
    dirname_only=$(dirname "$f");
    
    # use cut command to extract first 10 characters as date from filename
    date_only=$(echo "$fname_only" | cut -c 1-10);

    # use cut command to extract 11th character onward as new filename with extension
    fname_extract=$(echo "$fname_only" | cut -c 11-);

    # filename with complete path without extension 
    fname_no_extns="${fname_extract%.*}"

    # Now, move files to their desired format recursively
    mv "$f" "${dirname_only}/${fname_no_extns}.${date_only}_1.log"

done < loglist
10
  • 1
    (1) Please don’t do find *.  (2) Please don’t do find … > loglist / read f … < logfile.  (3) fname_complete is a peculiar name for a portion of the filename.  (4) You identify extension as a variable component of the filenames, but then your mv command assumes that the extension is .log. Aug 11, 2022 at 2:15
  • find * may be replaced with find "$(pwd)" -type f or find "`pwd`" -type f (backtick, not single quote). In this case we have been specifically dealing with files containing .log extension in their filename. Hence, mv command has no freedom to take other extension. Also fname_complete is just a variable, so it may be substituted with any name. But, it is not understood that why you don't recommend using while loop getting input from a file containing list of all desired files!
    – ESSPEE
    Aug 12, 2022 at 16:53
  • (1) If I want to insert a nail into a piece of wood, or a wall, I could use a glass bottle, a piece of fruit (a banana seems to be a useful shape), or my bare hand.  Or I could use a hammer.  By saying «find * may be replaced with find "$(pwd)"» (or find "$PWD") and not editing your answer, you make it sound like it’s all the same.  But the hammer is much better than the bottle or the banana, and find . (or, pretty much equivalently, find "$PWD") is better than find *. … (Cont’d) Aug 12, 2022 at 20:41
  • (Cont’d) …  find * (a) causes the shell to scan the directory, only to provide arguments to another program whose job it is to scan directories, (b) misses files whose names begin with . (unless you have enabled dotglob), and (c) can cause huge problems if you have files whose names begin with -.  (1+) Why do you believe that find * is equivalent to find "$(pwd)" -type f?  find * will find directories!  (2) read f will strip leading and trailing white space, will devour backslashes, and doesn’t stand a chance against filenames that contain newline.  … (Cont’d) Aug 12, 2022 at 20:41
  • (Cont’d) …  Look at the -exec directive in find.  It’s so much better to have find execute the desired command directly, than to have it write out filenames in an ambiguous way (filenames that contain newline) so they can then be read back in an inaccurate way. (4) OK, maybe I was nit-picking.  But the point remains, your comment says “filename, date and extension” (but not directory) while your code handles directory, filename and date (but not extension). So it’s not a big problem. But (a) your code doesn’t match your comment, which is always a bad thing, … (Cont’d) Aug 12, 2022 at 20:41
0

I don't know all possible cases for filenames, so I'll show you a general approach you can adapt to your needs.

You can loop through your files with a for loop or use find -exec as you like. I think you mainly want to know how to cut your filename to pieces and set them together differently.

You can either pipe the name into a tool like sed and create a regular expression to identify the pieces you need:

newname=`echo "$oldname"|sed -E 's/([0-9-]{10})(.*).log/\2_\1_1.log/'`

If you place () around those parts you can refer to them as \1, \2 and so on in the replacement string. If you need to adapt it, read the man page of sed or ask here if you don't succeed.

The other possibility is to use POSIX variable expansion:

You probably know that

${oldname#*201?}

will remove the date prefix of your filename (the ? can match any character, so this works for years 2010 to 2019, if that's sufficient. Otherwise adapt.)

You can also combine them:

${oldname%${oldname#*201?}}

Will remove the part we extracted above, so this will give you the date only. This way you can separate the parts of the filename and paste them in a different order.

4
  • Thanks. But I have few files with different , plz treat above above filename as file1 , file2 , file3, file4, case1, case2 , number1, number2...and so on. The word "file" is not fixed in all filename
    – Nirmal
    Aug 1, 2017 at 6:22
  • same format means - it should renamed as <filename>_<date>.1.log In other way , it should cut middle name which is after date and keep first and date middle and so on
    – Nirmal
    Aug 1, 2017 at 6:22
  • Of course you have multiple files. But by using a loop like for oldfile in *.log; do you can use the given commands as $oldfile will carry a different filename in each iteration.
    – Philippos
    Aug 1, 2017 at 6:38
  • My Org won't allow to use sed cmd. Are you asking me to try same as below- for oldfile in *.log; do mv $oldfile ${oldname%${oldname#*201?}}; done. Above will give strip the date only. can you help me in separating the parts of the filename and paste them in a different order which i mentioned in my edited question.
    – Nirmal
    Aug 1, 2017 at 6:45
0

The obligatory zsh + zmv answer:

autoload -Uz zmv
zmv -n '(<1-31>_<1-12>-<1900-2999>)(*).log' '$2.${1}_1.log'

Remove -n (for dry-run) if happy.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .