2

I have a number of files in a folder on a Linux machine with the following names:

11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35

I would like to use regex in order to rename with the .inp extension

I tried

mv * *.inp
mv: target '*.inp' is not a directory

which provided an error. I also tried using the regex [123][12345] instead of the *.

So, I understand that mv is used to move files around. I also got the idea that perhaps I could use ./*.inp to force mv to write in the same folder but it failed. So, apart from not understading correctly how mv works, how would I proceed to have have this done with mv?

2
  • 1
    What Linux are you using? Do you have th perl-rename (might be called rename or prename) command?
    – terdon
    Feb 1, 2022 at 13:53
  • Hello! I am using the following: LSB Version: :core-4.1-amd64:core-4.1-noarch Distributor ID: CentOS Description: CentOS Linux release 7.9.2009 (Core) Release: 7.9.2009 Codename: Core And yes, I have perl rename.
    – Strelok
    Feb 1, 2022 at 13:56

6 Answers 6

7

The issue with your command is that the mv command only can move/rename a single file (when given exactly two command line arguments), or move a bunch of files to a single destination directory (more than two command line arguments).

In your case, you use the expansion of * *.inp as the arguments, and this is going to expand to all the visible filenames in the current directory, followed by the names that matches *.inp. Assuming that this expands to more than two names, then the last argument needs to be the name of a directory for the command to be a valid mv command, or you'll get a "is not a directory" error.

In this case, we instead want to use mv with two arguments at a time, and for that we need to use a shell loop:

for name in [123][1-5]; do
    mv "$name" "$name.inp"
done

This loops over all names that matches (a variant of) the filename globbing pattern that you mentioned (note, this is not a regular expression). In the loop body, the current name will be stored in the name variable, and the mv simply renames the file by adding .inp at the end of the name.

This does not prevent mv from overwriting existing files in the case where there might be a name collision. For that, assuming you use GNU mv, you may want to use mv with its --no-clobber (or -n) option, or possibly with its --backup (or -b) option.

Or, you could do an explicit check for the existence of the destination name and skip the current file if it exists (which would also avoid moving files into exiting directories if you happened to have a directory with the same name as the destination name):

for name in [123][1-5]; do
    [ -e "$name.inp" ] || [ -L "$name.inp" ] && continue
    mv "$name" "$name.inp"
done

Using GNU mv with --no-target-directory (or -T) in combination with either -n or -b would avoid overwriting existing files (or back them up, with -b) and also avoid moving the files into a subdirectory that happened to have the same name as the destination name.

6

You can use perl-rename tool:

rename -n 's/$/.inp/' [0-9][0-9]

(or alternatively, use '$_ .= ".inp"' to append .inp, should be faster because no regex needs to be evaluated.)

Remove the -n if you're happy with the output.

0
3

With zsh's batch-renaming zmv function:

autoload -Uz zmv
zmv '[1-3][1-5]' '$f.inp'

The advantage over some of the other solutions mentioned here:

  • it will run some sanity checks first. For instance, if that could cause any data loss, it would abort the command before doing any renaming.
  • [1-5] is the same as [12345] in zsh. In bash and in the most common locales and on GNU systems, [1-5] would be more like [12345١٢٣٤۱۲۳۴߁߂߃߄१२३४১২৩৪੧੨੩੪૧૨૩૪୧୨୩୪௧௨௩௪౧౨౩౪౹౺౻౼౽౾೧೨೩೪൧൨൩൪෧෨෩෪๑๒๓๔໑໒໓໔༡༢༣༤༪༫༬༭၁၂၃၄႑႒႓႔፩፪፫፬១២៣៤៱៲៳៴᠑᠒᠓᠔᥇᥈᥉᥊᧑᧒᧓᧔᧚᪁᪂᪃᪄᪑᪒᪓᪔᭑᭒᭓᭔᮱᮲᮳᮴᱁᱂᱃᱄᱑᱒᱓᱔⁴₁₂₃₄⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅛⅜⅟①②③④⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓾❶❷❸❹❿➀➁➂➃➉➊➋➌➍➓〡〢〣〤㉈㉉㉊㉋㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㋀㋁㋂㋃㋉㋊㋋㍙㍚㍛㍜㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㏠㏡㏢㏣㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾꘡꘢꘣꘤꣑꣒꣓꣔꤁꤂꤃꤄꧑꧒꧓꧔꧱꧲꧳꧴꩑꩒꩓꩔꯱꯲꯳꯴1234𐄇𐄈𐄉𐄊𐅂𐅘𐅙𐅚𐅛𐅜𐅝𐅞𐋡𐋢𐋣𐋤𐌠𐏑𐏒𐒡𐒢𐒣𐒤𐡘𐡙𐡚𐡹𐡺𐡻𐡼𐢧𐢨𐢩𐢪𐢫𐣻𐤖𐤚𐤛𐧀𐧁𐧂𐧃𐩀𐩁𐩂𐩃𐩽𐪝𐫫𐭘𐭙𐭚𐭛𐭸𐭹𐭺𐭻𐮩𐮪𐮫𐮬𐳺𐹠𐹡𐹢𐹣𑁒𑁓𑁔𑁕𑁧𑁨𑁩𑁪𑃱𑃲𑃳𑃴𑄷𑄸𑄹𑄺𑇑𑇒𑇓𑇔𑇡𑇢𑇣𑇤𑋱𑋲𑋳𑋴𑑑𑑒𑑓𑑔𑓑𑓒𑓓𑓔𑙑𑙒𑙓𑙔𑛁𑛂𑛃𑛄𑜱𑜲𑜳𑜴𑣡𑣢𑣣𑣤𑱑𑱒𑱓𑱔𑱚𑱛𑱜𑱝𒐀𒐁𒐂𒐈𒐉𒐏𒐕𒐖𒐗𒐘𒐞𒐟𒐠𒐡𒐣𒐤𒐥𒐦𒐬𒐭𒐮𒐯𒐰𒐴𒐵𒐶𒐷𒐸𒐺𒐻𒐼𒐽𒐾𒐿𒑊𒑋𒑌𒑏𒑐𒑑𒑒𒑓𒑖𒑗𒑘𒑙𒑩𖩡𖩢𖩣𖩤𖭑𖭒𖭓𖭔𝍠𝍡𝍢𝍣𝟏𝟐𝟑𝟒𝟙𝟚𝟛𝟜𝟣𝟤𝟥𝟦𝟭𝟮𝟯𝟰𝟷𝟸𝟹𝟺𞣇𞣈𞣉𞣊𞥑𞥒𞥓𞥔🄂🄃🄄🄅🆛🆜🆝🆞🆢🆤]
  • it will not rename a file called literally [1-3][1-5] if there's no file that match that pattern.
1

How about this (in bash):

shopt -s extglob
for f in !(*.*); do mv -v "$f" "$f.inp"; done

With the extglob option enabled (this is what shopt -s extglob does), !(*.*) finds all files without an extension. If you want to find other files, that would need to change.

If you really need regular expressions, another way would be:

for f in $(find ./ -maxdepth 1 -regex '\.\/[123][12345]'); do mv -v "$f" "$f.inp"; done

@pLum0 says find should not be used in for loop. So this is also possible:

find ./ -maxdepth 1 -regex '\.\/[123][12345]' -exec mv -v "{}" "{}.inp" \;
5
  • 1
    What shell have you written this for? You also don't point out what the issue is with the user's command.
    – Kusalananda
    Feb 1, 2022 at 14:07
  • This is in bash.
    – nobody
    Feb 1, 2022 at 14:11
  • 1
    Then you probably also need shopt -s extglob, right? And possibly nullglob too?
    – Kusalananda
    Feb 1, 2022 at 14:12
  • You should not loop over find output... find has -exec.
    – pLumo
    Feb 1, 2022 at 14:19
  • @pLumo you're right, of course, but that wouldn't be a problem in this specific case since the find results are limited to names matching \.\/[123][12345].
    – terdon
    Feb 1, 2022 at 15:01
0

Use the mmv command if available for your distro.

See for example http://manpages.ubuntu.com/manpages/bionic/man1/mmv.1.html

0

Using rnr the command would be:

rnr -f '([0-9][0-9])' '${1}.inp' *

rnr has the benefit of being able to undo the command.

This site is temporarily in read-only mode and not accepting new answers.

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