Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rename file name contains backslash in bash?

Tags:

linux

bash

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

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

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.

like image 408
Daniel YC Lin Avatar asked Jun 12 '17 01:06

Daniel YC Lin


People also ask

How do you use backslash in filename?

The backslash divides the file name from the path to it, and one directory name from another directory name in a path. You cannot use a backslash in the name for the actual file or directory because it is a reserved character that separates the names into components.

How do I rename a file in bash?

You can also rename a file by using a command in bash script. Many commands exist in Linux to rename a filename. The command 'mv' is the most popular command for renaming a file. There is another command called 'rename' that can also be used for the same task.

Can a file name contain slash?

Characters to avoid in filenamesYou shouldn't be using slashes or question marks in your filenames. This can cause issues depending on the operating system/file system being used.

How do you name a file with a forward slash in Linux?

We can use the following characters in place of the regular forward-slash (U+002F): U+2044 = Fraction Slash: ⁄ U+2215 = Division Slash: ∕ U+29F8 = Big Solidus: ⧸


2 Answers

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 a\b
-rw-r--r--  1 ghoti  staff  0 11 Jun 23:13 c\d\e
$ for file in *\\*; do
> target="${file//\\//}"; mkdir -p "${target%/*}"; mv -v "$file" "$target"; done
a\b -> a/b
c\d\e -> 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.

like image 101
ghoti Avatar answered Oct 04 '22 04:10

ghoti


Renaming a file with backslashes is simple: mv 'a\b' 'newname' (just quote it), but you'll need more than that.

You need to:

  • find all files with a backslash (e.g. a\b\c)
  • split path from filename (e.g. a\b from c)
  • create a complete path (e.g. a/b, dir b under dir a)
  • move the old file under a new name, under a created path (e.g. rename a\b\c to file named c in dir a/b)

Something like this:

#!/bin/bash
find . -name '*\\*' | while read f; do
    base="${f%\\*}"
    file="${f##*\\}"
    path="${base//\\//}"
    mkdir -p "$path"
    mv "$f" "$path/$file"
done

(Edit: correct handling of filenames with spaces)

like image 25
randomir Avatar answered Oct 04 '22 02:10

randomir