In bash, I have an unzipped directory where I have many file names that start with _.

I'm pretty sure I can use rename or mv to simply remove the _ prefix of each file that has it.

I try:

for file; do
    mv "$file" "${file//_/}"

But that doesn't have the effect I'd like. What command can I use to remove the _ prefix from all files?

  • In exactly what way does this not have the effect you want? – dhag Feb 20 '18 at 22:00
  • Well, for one, for file; do <stuff>; done is a syntax error. – DopeGhoti Feb 20 '18 at 22:01
  • 1
    In once instance, it didn't remove any underscore prefixes. In another, it removed all underscores in every filename that starts with the _ prefix – obidyne Feb 20 '18 at 22:02
  • @DopeGhoti: for x; do is a shortcut for for x in "$@"; do, so, assuming that the code is in its own script file, this may work (if called as e.g. script.sh _*). I understand that's a lot off assumptions :). – dhag Feb 20 '18 at 22:06

1) for file; do loops over the positional parameters, i.e. command line arguments to the script. Use for file in *; do to loop over the files in the directory (or _* to just take the ones with an underscore prefix.)

2) You can use ${file#_} instead of ${file//_/} to remove the underscore from the beginning of the filename. ${par#word} specifically removes a part from the beginning, and it's a standard feature, unlike ${par/pat/repl}. And of course ${file//_} would remove all slashes (since you used a double-slash), not just the first one.

3) at least on Linux (GNU userland) and FreeBSD, you can use mv -n to ask it to not overwrite any files. Just in case.


for file in _*; do
    mv -n -- "$file" "${file#_}"

Simply with prename (Perl rename) command:

prename -n 's/^_//' _*

You're awfully close:

for file in _*; do
    if ! [[ -f "${file/_/}" ]]; then
        mv "$file" "${file/_/}"
        echo "Replacement for '$file' already exists; skipping.." 1>&2

Why ${file/_/}? Because you specified an underscore prefix. So _file_name.ext will become file_name.ext. With ${file//_/}, _file_name.ext would become filename.ext.

  • I'm still new to bash ^_^ – obidyne Feb 20 '18 at 22:04

You can use sed

for file in /some/path/*; do
    if [ -f "$file" ]; then
        mv "$file" `echo "$file" | sed 's*_**g'`
  • 1
    [ -e "$file" ] tests if a file of that name exists (regardless of its type). But you know it exists, since you just generated the name from a pattern match. – ilkkachu Feb 20 '18 at 22:07
  • @ilkkachu I was looking for -f. Wanted to know if it's a file and not a directory. My bad... – man0v Feb 20 '18 at 22:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.