user213544
user213544

Reputation: 2126

How to remove the last characters in a file name using Bash?

I have several .txt files in a folder, their names look as follows:

file1.txt
file2.txt
file2.txt_newfile.txt
file3.txt
file4.txt
file4.txt_newfile.txt
file5.txt_newfile.txt
...

I am trying to remove _newfile.txt from the file names. If the file is present, it should be overwritten by the new file (e.g. file2.txt will be replaced, but file5.txt will just be renamed).

Expected output:

file1.txt
file2.txt # this was file2.txt_newfile.txt
file3.txt
file4.txt # file4.txt_newfile.txt
file5.txt #file5.txt_newfile.txt
...

I tried the following code:

for i in $(find . -name "*_newfile.txt" -print); do 
mv -f "$file" "${file%????????????}"
done

However, I get the following error:

mv: rename  to : No such file or directory

What am I doing wrong and how can I rename these files?

Upvotes: 1

Views: 964

Answers (2)

ennox
ennox

Reputation: 206

For many people maybe an unusual solution, but for me this is a typical vi job. What many people might not remember, you can pipe the content of the vi buffer through a shell, even with debugging output (sh -x).

What I do in such or similar situations, where I do not want to loose too much time thinking about cool regular expressions or shell trickeries ... the pragmatic way ... vi supports you ;-)

Here we go:

1. enter directory with those files that you want to rename
2. start vi
3. !!ls *_newfile.txt
note: the !! command prompts you at the bottom to enter a command, the output of the ls command fills the vi buffer
4. dG
deletes all lines from your position 1 to the end of buffer, with a copy of it in the yank buffer
5. PP
paste 2 times the yank buffer
6. !G sort
!G prompts you at the bottom to pipe the buffer through sort
now you have all the lines double to save the work of typing filename again
7. with a combination of JkJk
you join the lines, so you have now the filename 2 times in a line like here:
file2.txt_newfile.txt file2.txt_newfile.txt
8. now add a mv command at the beginning of each line using an ex command
:%s/^/mv /
9. now remove the not needed trailing "_newfile.txt", again with an ex command
:%s/_newfile.txt$//
Now you have i.e. the following line(s) in the vi buffer:
mv file2.txt_newfile.txt file2.txt
10 back to line 1 to that you feed the whole buffer to the shell in the next step
1G
11. feed the shell commands to the shell and show some debug command
!G sh -x
12. check the results in the folder within vi, you will get the output of the ls command into the buffer
!!ls -l

Finally quit vi.

It looks maybe on the 1st glimpse like many steps, but if you know vi, then this goes blindingly fast and you have the additional advantage that you have all possibilities to save the instructions to a file for documentation purposes or to work things out to create a script, etc.

Upvotes: 1

oguz ismail
oguz ismail

Reputation: 50750

You're populating variable i with find's output, but referencing variable file, which is undeclared, in mv invocation. So it expands to mv -f '' '', that's why you get a No such file or directory error.

You better do that like this:

find -type f -name '*_newfile.txt' -exec sh -c '
for fname; do
  mv -- "$fname" "${fname%_newfile.txt}"
done' _ {} +

If these files are all in the same folder, you don't even need find, just a for loop will do the same:

for fname in *_newfile.txt; do
  mv -- "$fname" "${fname%_newfile.txt}"
done

Upvotes: 5

Related Questions