1

Although perl's rename tool has become an invaluable companion for my daily work, there are still some quirks that make work more difficult - especially when files are not in current work directory.

Let's suppose there are lots of invoices, and we know that all from customer1 are already assigned to an account. So, to make script processing easier later, we want to prepend assi_ for "assigned".

For example:

/home/user/bizz/invoices $ ls -1 t1/inv*

t1/inv_customer1_20130506.txt
t1/inv_customer1_20130823.txt
t1/inv_customer1_20130930.txt
t1/inv_customer2_20131207.txt
t1/inv_customer2_20131113.txt

Now look what will happen:

/home/user/bizz/invoices $ rename 's/^/assi_/' t1/inv_customer1*

Can't rename t1/inv_customer1_20130506.txt assi_t1/inv_customer1_20130506.txt: No such file or directory
(etc.)

Heheh. rename has chosen the whole path for prepending assi_ to, instead of only the file. So no wonder it cannot find inv_customer1_20130506.txt in the non-existing directory "assi_t1".

So how can rename be taught to apply the regular expression to the file name only?

Notes:
1. I don't always want to cd to the subdirectories.
2. I would refrain from using loops as well as find, if possible.

syntaxerror
  • 2,246
  • What you can say if rename 's/\//\/assi_/' t1/inv_customer1* – Costas Nov 08 '14 at 21:48
  • Or rename 's/inv/assi_inv/' t1/inv_customer1* – Costas Nov 08 '14 at 21:49
  • Sure, but this test case is in fact simplified and, above all, showcase. The solution I'm looking for should always be able to prepend a prefix to a filename w/ignoring the directory, and not just by quoting "hard" strings as in your second proposal. – syntaxerror Nov 08 '14 at 21:52
  • Do it clear for you what you want? ;) (cd t1 ; rename 's/^/assi_/' inv_customer1*) is acceptable to you? – Costas Nov 08 '14 at 22:18
  • 1
    Or rename 's|([^/]+$)|assi_$1|' t1/inv_customer1* you'd like more? – Costas Nov 08 '14 at 22:49
  • Proposal #3 is totally unacceptable, since I wrote in my OP clearly that I do not want to cd to the subdirectory. (be glad that we cannot vote -1 on comments :). At least it would encourage certain folks to read the OP more thoroughly.) However, thanks for proposal #4, which is good and which could maybe do the trick indeed. – syntaxerror Nov 09 '14 at 01:01
  • Regarding #3 please take into attention the brackets around expression. So the command execute in the subprocess and after it the $PWD remains the same. – Costas Nov 09 '14 at 09:49

1 Answers1

2

rename works this way because it can move files between directories. Like mv, it acts on the whole path, not just on the last component.

If you only want to modify the last component, you can anchor your regexp at (\A|?<=/), and make sure that it doesn't match any / and only matches at the last /.

rename 's~(\A|?<=/)(?=[^/]*)\z~assi_~' t1/inv_customer1*

Another approach is to first split the file name.

rename 's~(.*/)~~s; my $d = $1; s/\A/assi_/; $_ = "$d$_"' t1/inv_customer1*

You can also use the File::Spec module. This has the advantage of coping with non-Unix paths.

rename 'use File::Spec::Functions; my ($v,$d,$f) = splitpath($_); $f =~ s/\A/assi_/; $_ = catpath($v,$d,$f)' t1/inv_customer1*