Skip to content
Advertisement

How to rename file name contains backslash in bash?

I got a tar file, after extracting, there are many files naming like

a
bc
def
gh

I want to correct their name into files in sub-directories like

a
b/c
d/e/f
g/h

I face a problem when a variable contains backslash, it will change the original file name. I want to write a script to rename them.

Advertisement

Answer

Parameter expansion is the way to go. You have everything you need in bash, no need to use external tools like find.

$ touch a\b c\d\e
$ ls -l
total 0
-rw-r--r--  1 ghoti  staff  0 11 Jun 23:13 ab
-rw-r--r--  1 ghoti  staff  0 11 Jun 23:13 cde
$ for file in *\*; do
> target="${file//\//}"; mkdir -p "${target%/*}"; mv -v "$file" "$target"; done
ab -> a/b
cde -> c/d/e

The for loop breaks out as follows:

  • for file in *\*; do – select all files whose names contain backslashes
  • target="${file//\//}"; – swap backslashes for forward slashes
  • mkdir -p "${target%/*}"; – create the target directory by stripping the filename from $target
  • mv -v "$file" "$target"; – move the file to its new home
  • done – end the loop.

The only tricky bit here I think is the second line: ${file//\//} is an expression of ${var//pattern/replacement}, where the pattern is an escaped backslash (\) and the replacement is a single forward slash.

Have a look at man bash and search for “Parameter Expansion” to learn more about this.


Alternately, if you really want to use find, you can still take advantage of bash’s Parameter Expansion:

find . -name '*\*' -type f 
  -exec bash -c 't="${0//\//}"; mkdir -p "${t%/*}"; mv -v "$0" "$t"' {} ;

This uses find to identify each file and process it with an -exec option that basically does the same thing as the for loop above. One significant difference here is that find will traverse subdirectories (limited by the -maxdepth option), so … be careful.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement