18

I'm trying to rename all files in current directory such that upper case name is converted to lower. I'm trying to do it like this:

ls -1|gawk '{print "`mv "$0" "tolower($0)"`"}'|xargs -i -t eval {}

I have two files in the directory, Y and YY -t added for debugging, and output is:

eval `mv Y y`
xargs: eval: No such file or directory

if I execute the eval on its own, it works and moves Y to y.

I know there are other ways to achieve this, but I'd like to get this working if I can! Cheers

skaffman
  • 398,947
  • 96
  • 818
  • 769
Joe Watkins
  • 1,593
  • 4
  • 15
  • 25

4 Answers4

28

eval is a shell builtin command, not a standalone executable. Thus, xargs cannot run it directly. You probably want:

ls -1 | gawk '{print "`mv "$0" "tolower($0)"`"}' | xargs -i -t sh -c "{}"
Cyrus
  • 1,216
  • 1
  • 8
  • 12
Frédéric Hamidi
  • 258,201
  • 41
  • 486
  • 479
  • 5
    Realised you don't need the eval (or the `s) with this method. so ls -1 | gawk '{print "mv "$0" "tolower($0)}' | xargs -i -t sh -c "{}" works as well – Joe Watkins Nov 12 '10 at 09:12
  • it doesn't work for me when coming from a sed replace which outputs a docker pull xx: `sed -n 's/\s*image: \(.*\)/docker pull \1/gp' ../docker-compose.yml |xargs -i -0 -t sh -c "{}"`. I get 'Invalid reference format'. I also tried putting double quotes before the xargs – tuxErrante Nov 10 '20 at 15:17
  • Using sh -c unfortunately doesn't preserve the shell variables that aren't exported, as it executes in a new process – bozzle Apr 29 '22 at 05:56
4

Although you're looking at an xargs solution, another method to perform the same thing can be done with tr (assuming sh/bash/ksh syntax):

for i in *; do mv $i `echo $i | tr '[A-Z]' '[a-z]'`; done
Chris J
  • 30,688
  • 6
  • 69
  • 111
  • yes, that's probably the best one - it a colleague's similar code, but split over several lines, and using a variable - which prompted the question - can you do it in 1 line? I then got distracted by trying to get xargs to work – Joe Watkins Nov 11 '10 at 12:53
  • Yep -- what I've dropped above will work without modification. No matter what you're dealing with, semi-colons are the statement separator if you want to combine multiple statements on one line. – Chris J Nov 12 '10 at 09:11
1

If your files are created by creative users, you will see files like:

My brother's 12" records

The solutions so far do not work on that kind of files. If you have GNU Parallel installed this will work (even on the files with creative names):

ls | parallel 'mv {} "$(echo {} | tr "[:upper:]" "[:lower:]")"'

Watch the intro video to learn more: http://www.youtube.com/watch?v=OpaiGYxkSuQ

Ole Tange
  • 1,990
  • 16
  • 10
0

You can use eval with xargs like the one below.

Note: I only tested this in bash shell

ls -1| gawk '{print "mv "$0" /tmp/"toupper($0)""}'| xargs -I {} sh -c "eval {}"

or

ls -1| gawk '{print "mv "$0" /tmp/"toupper($0)""}'| xargs -I random_var_name sh -c "eval random_var_name"

I generally use this approach when I want to avoid one-liner for loop.

e.g.

for file in $(find /some/path | grep "pattern");do somecmd $file; done

The same can be written like below

find /some/path | grep "pattern"| xargs -I {} sh -c "somecmd {}"
k_vishwanath
  • 1,326
  • 2
  • 20
  • 28