3

I am writing a script that requires me to provide a list of files in my directory, whose filenames contain spaces,*,?,$,%,etc. How can I do this, I have seen multiple posts but couldn't find anything that works for me. Is it possible to achieve this using grep?

4 Answers 4

3
printf '<%s>\n' *[[:space:]*?$%]*

It will not report hidden files. Whether [[:space:]] will match multi-byte spacing characters (like " " (EM QUAD) in UTF-8) depends on your shell (some like dash are not multi-byte character aware yet)

Example:

$ touch sane 'a%b' $'with\nnewline' $'with space' $'with em-quad'
$ printf '<%s>\n' *[[:space:]*?$%]*
<a%b>
<with em-quad>
<with
newline>
<with space>
1
  • If the order of characters inside the character class are changed, specially if the $ is in front of any variable of the shell, it may be expanded. As with printf '<%s>\n' *[?$*%[:space:]]*. It would be safer if the idea of quoting inside the character class is comented.
    – user79743
    Sep 25, 2015 at 22:09
2

A safe list of files is produced with an unquoted *, no find used:

$ echo *

Let's have some files to test:

$ mkdir test-dir; cd test-dir
$ touch clean normal_file 'a%b' 'has two spaces' '*' 'a*b' '?' 'a?b'
$ touch '$' 'a$b' '%' 'a%b' $'new\nline' $'em\u2001quad' 'normal_file'

The echo * command will produce, with the files above:

? $ * % a?b a$b a*b a%b clean em quad has two spaces new
line normal_file

A list separated by space, which, yes, has two lines, as there is a file with a new-line in it's name.

An easier way to see the files is to quote their name with printf '%q' and adding a new line, like printf '%q\n' we get a clean list of files:

$ printf '%q\n' *
\?
\$
\*
%
a\?b
a\$b
a\*b
a%b
clean
$'em\342\200\201quad'
has\ two\ spaces
$'new\nline'
normal_file

Of course, you need to select some files, not all, as with plain *.
So, we create a pattern of all characters to match: ?$*%.
However, most of those characters are 'special' and need quoting when appear in some orders. Specifically, if * or ? follows $, they are expanded by the shell, which could create problems.
It is imperative to quote the $.
Then, an easy way to get all the other characters quoted is to use printf '%q' ?\$*%. That will produce: \?\$\*%. If the backslash is not strictly needed, it will produce no ill effect, the escaped character will still work.
To that list we need to add some blank characters. But the inclusion of new-line becomes a serious problem. The easiest way to deal with new-line inside a character class is to use [[:space:]], which will include new-line, space, tab, and some other space characters as em-dash.

Therefore, the command becomes:

$ printf '%q\n' *[\?\$\*%[:space:]]*
\?
\$
\*
%
a\?b
a\$b
a\*b
a%b
$'em\342\200\201quad'
has\ two\ spaces
$'new\nline'

Please note that clean and normal_file file names were not listed, as required.

1

grep -E should do what you need. Note that some characters need to be escaped with a backslash, so if you add more and the results aren't what you expected, escape it and try again. Note the | is an or...

$ ls | grep -E '\s|\*|\?|\$|%'
all this.txt
all?this.txt
all*this.txt
all%this.txt
all th*s.txt
all$.txt
9
  • grep -E ... ? (extended regexp, gnu grep 2.16 here)
    – Hannu
    Sep 25, 2015 at 16:25
  • Yes grep -E works
    – KLMM
    Sep 25, 2015 at 16:26
  • Updated to -E. I suspect that's more common. I just use -P for specific things, and rarely use -E. Cheers.
    – stevieb
    Sep 25, 2015 at 16:29
  • 1
    grep -E '\*' file will print all lines in the file that contain *
    – stevieb
    Sep 25, 2015 at 16:31
  • 1
    Also note that if will fail to report file names that contain newline character (newline being a spacing character matched by \s and [[:space:]]). Sep 25, 2015 at 16:37
0

This is really simple and worked already with the Bourne Shell from 1979:

for i in *
do
    case "$i" in
        *\ *) echo blank $i;;
        *\?*) echo qmark $i;;
    esac
done

in one line:

for i in *; do
    case $i in
        *\ *|*\?*|*\**|*\$*|*%* ) echo "Special character $i"; ;;
        * ) echo "normal file $i"; ;;
    esac
done

Add more cases if you like...

0

You must log in to answer this question.

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