Renaming files - error

set -vx

for i in ~/Shell_Practice/rename/*.txt 
do  
    t=`(echo $i | awk gsub("file","newfile"))`
    mv $i $t
 done

while running the above code getting below error.Please help.

atomic@atomic-VirtualBox:~/Shell_Practice/rename$ ./rename.sh

for i in ~/Shell_Practice/rename/*.txt 
do  
    t=`(echo $i | awk gsub("file","newfile"))`
    mv $i $t
 done
++ for i in ~/Shell_Practice/rename/*.txt
./rename.sh: command substitution: line 5: syntax error near unexpected token `('
./rename.sh: command substitution: line 5: `(echo $i | awk gsub("file","newfile"))'
++ t=
++ mv /home/atomic/Shell_Practice/rename/file10.txt
mv: missing destination file operand after '/home/atomic/Shell_Practice/rename/file10.txt'
Try 'mv --help' for more information.
++ for i in ~/Shell_Practice/rename/*.txt
./rename.sh: command substitution: line 5: syntax error near unexpected token `('
./rename.sh: command substitution: line 5: `(echo $i | awk gsub("file","newfile"))'
++ t=
++ mv /home/atomic/Shell_Practice/rename/file1.txt
mv: missing destination file operand after '/home/atomic/Shell_Practice/rename/file1.txt'
 Try 'mv --help' for more information.

******************************
file names

atomic@atomic-VirtualBox:~/Shell_Practice/rename$ ls
file10.txt  file2.txt  file4.txt  file6.txt  file8.txt  rename.sh
file1.txt   file3.txt  file5.txt  file7.txt  file9.txt

There are almost certainly much simpler ways to do what you're trying to do, but we need to know what operating system and shell you're using to be able to suggest a solution that will work in your environment. Whenever you start a new thread in UNIX for Beginners Questions & Answers forum, it always helps us help you if you tell us what operating system and shell you're using!

I note that you're trying to use awk 's gsub() function which will replace all occurrences of file with newfile . And I note that none of your filenames contains more than one occurrence of that string. If that string does occur more than once in a filename, do you really want to replace all of them? Or, do you just want to change the first occurrence?

The error you're getting is because you haven't quoted the awk script operand and the shell is seeing the parentheses on the call to gsub() as an attempt to invoke a sub-shell at a spot where a sub-shell is not valid.

If you fix that and you ever try passing a filename to that script that does not contain the string file , the mv command for that file will fail because the variable t will be set to an empty string. If you make sure that your awk script will always return the original filename if no occurrences of the string file were found, you would end up trying to issue a command similar to:

mv a.txt a.txt

which will be a relatively slow no-op; but at least it won't cause your script to issue diagnostics for missing operands.

The command substitution you're using is an obsolescent form (unless you're using a pure Bourne shell from the 1970's. And, there is no reason to invoke a sub-shell in your command substitution.

Does the operating system you're using include a utility named rename ?

1 Like

In many operating systems, there is a utility in the repositories - a script written in Perl. Copes with such tasks very well.

prename 's/file/newfile/' ~/Shell_Practice/rename/*.txt

--- Post updated at 19:00 ---

Oh, at first it's better to look at the output without real renaming.

prename -vn 's/file/newfile/' ~/Shell_Practice/rename/*.txt
1 Like

HI Don,

Thanks for the reply.

My OS have rename command, however i want to solve that by using sed or awk.

I need to change all file names. My system details below.

atomic@atomic-VirtualBox:~$ uname -a
Linux atomic-VirtualBox 4.15.0-42-generic #45-Ubuntu SMP Thu Nov 15 19:32:57 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
atomic@atomic-VirtualBox:~$ echo $SHELL
/bin/bash

Thanks N

The necessary modifications to your own loop were presented to you, to make it run, and to improve it. However, no sed nor awk are needed to solve your problem. Just use shell's "parameter expansion", and you're there:

for i in ~/Shell_Practice/rename/*.txt
  do    t=${i/"file"/"newfile"}
        [ ! "$i" = "$t" ] && echo mv "$i" "$t"
  done

you could use an if ... then ... fi construct as well. Remove the echo if you're happy with what you see.

2 Likes

Try something like:

for i in ~/Shell_Practice/rename/*.txt 
do  
  if [ -e "$i" ]; then
    mv -- "$i" "${i/file/newfile}"
  fi
done

or

shopt -s nullglob
for i in ~/Shell_Practice/rename/*.txt 
do  
  mv -- "$i" "${i/file/newfile}"
done
2 Likes

You never answered the question of whether a file named filexfile.txt should be moved to newfilexfile.txt or to newfilexnewfile.txt . And, I'm disappointed that you didn't try to rewrite your code based on the comments I provided in post #2 in this thread. But, I did make (at least) one mistake in that post. If you run the command:

mv file1 file2

where file1 and file2 are the same and name an existing file or if file1 and file2 are different but both names are pathnames that refer to the same file (because they are linked together), the standards require that one of three things happen:

  1. the request is silently ignored,
  2. a diagnostic message is printed, no change is made to the file, and the final exit status of mv will be non-zero, or
  3. if the two operands are distinct (but name the same file), the file1 will be removed. (Note that if file2 is a symbolic link pointing to file1 and file1 is not a symbolic link, this may result in the loss of your data.)

Note that none of the suggestions that have been posted in this thread (including the two suggestions below) make any attempt to verify that the new pathname you want to use is not linked to the current pathname. The code below at least makes sure that when mv is invoked, the two operands won't be the same string. If there is a chance that the old and new names you are trying to create for a file could refer to the same file, I leave it to you to make adjustments to the code if any system on which you run your script has a mv utility that does not behave the way you want it to behave.

If you really don't want to use the shell's "${path/file/newfile}" variable expansion because it is not in the standards yet or because you really do want to be able to replace multiple occurrences of file with newfile if you run into a pathname containing multiple occurrences of that string, and you really want to use

awk

or

sed

to solve this problem; at least consider something like the following so you aren't invoking awk or sed and a shell for each pathname you process and so you don't invoke mv to move a file whose name won't change because it didn't contain the string file to begin with:

#!/bin/bash
set -vx

for path in ~/Shell_Practice/rename/*file*.txt 
do	[ -e "$path" ] && printf '%s\n' "$path"
done | awk '
{	old = $0
	gsub(/file/, "newfile")
	print "echo mv --", "\""old"\"", "\""$0"\""
}' | bash -xv

or (preferably):

#!/bin/bash
set -vx

for path in ~/Shell_Practice/rename/*file*.txt 
do	[ -e "$path" ] && printf '%s\n' "$path"
done | sed 'p; s/file/newfile/g' | while read -r oldpath && read -r newpath
do	echo mv -- "$oldpath" "$newpath"
done

If one of these prints the mv commands you want to invoke, remove the word echo from the script and run it again to actually rename the files.

Obviously, the awk script could also be rewritten to get rid of the second invocation of bash . I will leave doing that set of trivial changes as an exercise for the reader.

1 Like

Thanks Rudic.

It working perfectly.Can you explain the below code.

 t=${i/"file"/"newfile"}

--- Post updated at 12:27 PM ---

Hi Don,

Apologies for delay message. My request is simple and below shown and rudic code worked.

i/p

atomic@atomic-VirtualBox:~/Shell_Practice/rename$ ls -lrt
total 8
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file2.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file1.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file3.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file4.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file6.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file5.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file7.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file8.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file9.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 file10.txt
-rwxr-xr-- 1 atomic atomic 117 Jan 26 22:08 rename.sh
-rwxr-xr-- 1 atomic atomic 125 Jan 30 21:36 myrename.sh

output

atomic@atomic-VirtualBox:~/Shell_Practice/rename$ ls -lrt
total 8
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile2.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile1.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile3.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile4.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile6.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile5.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile7.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile8.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile9.txt
-rw-r--r-- 1 atomic atomic   0 Jan 25 22:55 newfile10.txt
-rwxr-xr-- 1 atomic atomic 117 Jan 26 22:08 rename.sh
-rwxr-xr-- 1 atomic atomic 121 Jan 30 21:38 myrename.sh

As I said in my post, that's called "parameter expansion" in shell. man bash :