I have a folder full of text files of the format 'four digits, space, alphanumeric string.txt'. For example:
$ touch '0001 jko0001.txt' '0002 jko0002.txt' '0003 jko0003.txt'
$ ls
'0001 jko0001.txt' '0002 jko0002.txt' '0003 jko0003.txt'
I would like to rename the files so leading digits and space are removed. Since I have a lot of files, I am using find
to pass filenames to rename
. I attempted to do this with the following command:
find . -type f -name '*.txt' -print0 | xargs -0 rename -n -- 's/^\d{4}\s+//' {} +
However, this fails. (Yes, -n
is only to print out changes without renaming the files. It fails even if I remove it).
Interestingly, if I split the command into pieces, it does work:
$ find . -type f -name '*.txt'
./0003 jko0003.txt
./0002 jko0002.txt
./0001 jko0001.txt
$ rename -n -- 's/^[0-9]{4}\s+//' *.txt
0001 jko0001.txt -> jko0001.txt
0002 jko0002.txt -> jko0002.txt
0003 jko0003.txt -> jko0003.txt
$ bash --version
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
But when combined with xargs
, it fails. Why?
Also, I can't even get it working with find
's -execdir
:
find . -type f -name '*.txt' -execdir rename -n -- 's/^\d{4} //' {} \;
None of these work.
find . -type f -name '*.txt' -print0 | xargs -0 rename -n -- 's/^\d{4}\s+//' "{}" +
find . -type f -name '*.txt' -print0 | xargs -0 rename -n -- 's/^[0-9]{4}\s+//' "{}" +
find . -type f -name '*.txt' -execdir rename -n -- 's/^\d{4} //' {} \;
find . -type f -name '*.txt' -execdir rename -n -- 's/^\d{4} //' '{}' \;
Thanks in advance!
There are at least two problems here. First, find
is passing a path to the file, not just the filename (yes, even with -execdir
). So add the -d
option to rename
, to tell it to just act on just the filename, not the full path.
Second, you're mixing find -exec
syntax up with xargs
. Specifically, the {} +
at the end of the command is something you'd use with find -exec
, not with xargs
(note: in some modes, xargs
uses {}
like this, but it never uses +
). To fix it, either remove the {} +
and use standard xargs
syntax:
$ find . -type f -name '*.txt' -print0 | xargs -0 rename -n -d -- 's/^\d{4}\s+//'
rename(./0003 jko0003.txt, ./jko0003.txt)
rename(./0001 jko0001.txt, ./jko0001.txt)
rename(./0002 jko0002.txt, ./jko0002.txt)
Or skip xargs
, and use find -exec
directly (this time with the {} +
):
$ find . -type f -name '*.txt' -exec rename -n -d -- 's/^\d{4}\s+//' {} +
rename(./0003 jko0003.txt, ./jko0003.txt)
rename(./0001 jko0001.txt, ./jko0001.txt)
rename(./0002 jko0002.txt, ./jko0002.txt)
BTW, when troubleshooting problems like this, it's sometimes helpful to put echo
in front of the problem command to get an idea what arguments are being passed to it:
$ find . -type f -name '*.txt' -print0 | xargs -0 echo rename -n -- 's/^\d{4}\s+//' {} +
rename -n -- s/^\d{4}\s+// {} + ./0003 jko0003.txt ./0001 jko0001.txt ./0002 jko0002.txt
^^^^ this is the problem
But that's sometimes misleading, since (among other things) it loses the distinction between spaces within arguments and spaces between arguments. Replacing echo
with printf '%q\n'
is sometime better (although it has other problems, and not all external printf
implementations support %q
).