3

I would expect under bash for the following:

 ls *.py | xargs -I{} echo $(echo {} | sed 's/\.py/_2\.py/g')

to list all .py files contained in the directory but with _2 annexed after the file name and before the .py extension.

Yet this is not what happens. It simply lists the .py files in the directory without any change.

In short:

$ ls
A.py  B.py

$ ls *.py | xargs -I{} echo $(echo {} | sed 's/\.py/_2\.py/g')
A.py
B.py

while the output I expected would've been:

A_2.py
B_2.py

What happens here and how to get the expected output?

5
  • Why not ls *.py | xargs -I{} echo {} | sed 's/\.py/_2\.py/g'?
    – Philippos
    Commented Aug 30, 2019 at 8:06
  • 1
    @Philippos well, for this even ls *.py | sed 's/\.py/_2\.py/g' works... the reason is that this is testing, I want to replace the echo after the -I{} with a command like cp, mv, ...
    – Amaterasu
    Commented Aug 30, 2019 at 8:09
  • 1
    In that case, don't parse the output of ls and use find ... -exec ... instead. See also unix.stackexchange.com/q/128985/364705
    – markgraf
    Commented Aug 30, 2019 at 8:11
  • 1
    @Amaterasu Depends on what you do in the exec :-) find . -type f -name '*.py' -exec sh -c 'a={}; mv "$a" "${a%.py}_2.py"' \;
    – markgraf
    Commented Aug 30, 2019 at 8:38
  • @markgraf yeah, well that works and you can have a "preview" what files will be renamed. find -name "*.py" -exec cp -n {} {}_2 \; -exec rename .py_2 _2.py {} \; otoh is less baroque but it can't be tested ... and is it really safe?
    – Amaterasu
    Commented Aug 30, 2019 at 9:00

1 Answer 1

3

This happens because the command in subshell i.e.

echo {} | sed 's/\.py/_2\.py/g'

executes before echo, so after this command the command will be:

xargs -I{} echo {}

which will simply echo all the file_names.

You can use find command:

find directory_name -name '*.py' \
-exec rename 's/.py/_2.py/' {} +

It will rename all the files in just one command.

To find files in current directory only not subdirectories, use maxdepth 1 option i.e.:

find directory_name -maxdepth 1 -name '*.py' \
-exec rename 's/.py/_2.py/' {} +
4
  • Can you also copy with that command rename in one pass?
    – Amaterasu
    Commented Aug 30, 2019 at 8:32
  • Note that the user is only accessing .py files in the current directory. This makes the use of find unnecessary.
    – Kusalananda
    Commented Aug 30, 2019 at 8:33
  • @Amaterasu this command will be like rename 's/.py/_2.py/' file1.py file2.py ...., see stackoverflow.com/questions/29360925/…
    – Prvt_Yadav
    Commented Aug 30, 2019 at 8:53
  • @Amaterasu no, but you can have more than one -exec in a single find command. e.g. -exec cp ... \; first, followed by -exec rename ... +. By default, the second -exec (rename) will only be run for files where the first (cp) returned true.
    – cas
    Commented Aug 30, 2019 at 10:05

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