Reputation: 361
When I use the "tab" key in bash
(when you have started to type the filename and you want it to complete), bash
escapes the filename correctly, and if I use exactly that "escaped" filename, it works.
For Instance:
An-Beat - Mentally Insine (Original Mix).mp3
=> After bash
Escapes It Using "TAB"
An-Beat\ -\ Mentally\ Insine\ \(Original\ Mix\).mp3
I'm search for a function for bash
that will escape a filename the same way "tab" escapes filenames.
Upvotes: 35
Views: 43136
Reputation: 316
I'm search for a function for bash that will escape a filename the same way "tab" escapes filenames.
I've created a portable function to get all the escaped paths to all items in the current directory, tested on macOS and Linux (requires GNU bash installed).
escpaths() {
find "$PWD" -maxdepth 1 -print0 | xargs -0 -I {} bash -c 'printf "%q\n" "$0"' {} | sort
}
Here's a rigorous test case scenario:
# Generate test directory, containing test directories and files.
mkdir 'test dir'; cd 'test dir'
mkdir '\dir with\ backslashes\\\'
touch '\file with \\backslashes\'
touch 'An-Beat - Mentally Insine (Original Mix).mp3'
mkdir 'crazy(*@$):{}[]dir:'
mkdir 'dir with 字 chinese 鳥鸟 characters'
touch $'file\twith\tmany\ttabs.txt'
touch 'file @*&$()!#[]:.txt'
touch 'file
with
newlines.txt'
Executing escpaths
in the test directory test dir
gives the escaped output:
$'/.../test dir/file\nwith\nnewlines.txt'
$'/.../test dir/file\twith\tmany\ttabs.txt'
/.../test\ dir
/.../test\ dir/An-Beat\ -\ Mentally\ Insine\ \(Original\ Mix\).mp3
/.../test\ dir/\\dir\ \ with\\\ backslashes\\\\\\
/.../test\ dir/\\file\ with\ \\\\backslashes\\
/.../test\ dir/crazy\(\*@\$\):\{\}\[\]dir:
/.../test\ dir/dir\ with\ 字\ chinese\ 鳥鸟\ characters
/.../test\ dir/file\ @\*\&\$\(\)\!#\[\]:.txt
This (also portable) function will get you the escaped basenames of all items in the current directory (this time excluding the current directory).
escbasenames() {
find . -mindepth 1 -maxdepth 1 -exec printf '%s\0' "$(basename {})" \; | xargs -0 -I {} bash -c 'printf "%q\n" "${0#./}"' {} | sort
}
Running escbasenames
in the same test directory test dir
gives the escaped basenames:
$'file\nwith\nnewlines.txt'
$'file\twith\tmany\ttabs.txt'
An-Beat\ -\ Mentally\ Insine\ \(Original\ Mix\).mp3
\\dir\ \ with\\\ backslashes\\\\\\
\\file\ with\ \\\\backslashes\\
crazy\(\*@\$\):\{\}\[\]dir:
dir\ with\ 字\ chinese\ 鳥鸟\ characters
file\ @\*\&\$\(\)\!#\[\]:.txt
Note that if the path/filename contains newlines or tabs, it will be encoded as an ANSI-C string. Autocompletion in the terminal also completes with ANSI-C strings. Example ANSI-C string autocompletion outputs would look like my$'\n'newline$'\n'dir/
or my$'\t'tabbed$'\t'file.txt
.
Upvotes: 1
Reputation: 43
I may be a little late to the party but what worked for me is:
ls --quoting-style=shell-escape
This way it also escapes characters like !
or '
.
Upvotes: 2
Reputation: 41
ls --quoting-style=escape /somedir
this will output the escaped filenames, and also work with unicode characters, printf method does not work with Chinese, it outputs something like $'\206\305...'
Upvotes: 4
Reputation: 2315
The solution from "sehe" works fine, in addition, you can also use double quotes (") instead of single apostrophe (') to by able to use variables:
x="a real \good %* load of crap from ${USER}"
echo $(printf '%q' "$x")
Of course the string may not contain $ or " itself or you have to escape those manulally by splash \$.
Upvotes: 4
Reputation: 370
I'm going to elaborate on sehe's response on this one.
If you want to pass the argument to be converted as a shell script parameter, encase the parameter in "'s.
#!/bin/bash
x=$(printf '%q' "$1")
echo $x
I really like the printf solution, since it does every special character, just like bash.
Upvotes: 10
Reputation: 392929
Use printf
(1):
x='a real \good %* load of c$rap'
x=$(printf '%q' "$x")
echo $x
will return
a\ real\ \\good\ %\*\ load\ of\ c\$rap
Upvotes: 50
Reputation: 25599
$ string="An-Beat - Mentally Insine (Original Mix).mp3"
$ echo ${string// /\\ }
An-Beat\ -\ Mentally\ Insine\ (Original\ Mix).mp3
$ string=${string// /\\ }
$ echo ${string//(/\\( }
An-Beat - Mentally Insine \( Original Mix).mp3
Upvotes: 4