0

How can find search results containing spaces to be printed with enclosing '' (a quote pair). Only if this result contains spaces, if there are not spaces in the path string, to remain as is.

thanasisp
  • 8,122
  • 3
    What do you intend to do with the result? The recommended way of handling "unusual" filenames for further processing is to use -print0 if your find implementation supports it – steeldriver Oct 28 '20 at 01:49
  • ... otoh if it's just for display purposes then at least with GNU find you could do something like -path '* *' -printf '"%p"\n' -o -print I suppose – steeldriver Oct 28 '20 at 04:31
  • 1
    Even without -print0 you can reliably do something to a file (with any valid name) thanks to -exec. – Kamil Maciorowski Oct 28 '20 at 06:18

3 Answers3

2

Here is a way to do it using just find if your version supports -printf.

find . -name "* *" -printf "\"%p\"\n" -o -print
bdowling
  • 136
  • 4
  • Note: From Stéphane Chazelas, you may want to set LC_ALL=C before this command so that the '*' will match bytes that are not valid characters. That is probably not necessary 99.9% of the time, so I think it should be here as a note instead of in the answer. – bdowling Oct 28 '20 at 07:13
  • Edited. Thanks, Stéphane. That is probably what most people would want. – bdowling Oct 29 '20 at 08:23
1

Assuming you want to generate CSV output, with GNU tools:

find . -print0 | 
  LC_ALL=C sed -z '/[",[:space:]]/{s/"/""/g; s/.*/"&"/}' |
  tr '\0' '\n'

That is, as long as the file name contains at least one ", , or ASCII whitespace character (including, but not limited to SPC, TAB, CR and newline all special in CSV), translate " to "" (which is how most CSV formats escape the "s), and enclose the string in double quotes.

That part is done using NUL as the record delimiter since it's the only byte that can't occur in a file path. And then, we translate NULs to newline with tr.

On non-GNU systems, you can always resort to perl:

find . -print0 | perl -l -0 -pe 'if (/[",\s]/) {s/"/""/g; $_ = "\"$_\""}'

After

touch 'a b' $'a\nb' a,b a_b $'a\200b' 'a"b'

They give:

.
"./a""b"
./a_b
"./a,b"
./a�b
"./a
b"
"./a b"

(where is my terminal's rendition of that \200 byte that doesn't form a valid character in my UTF-8 locale).

To exclude . and the ./ prefix, replace -print0 with -mindepth 1 -printf '%P\0' (though -printf is GNU-specific). Which gives:

"a""b"
a_b
"a,b"
a�b
"a
b"
"a b"

-print0 and -mindepth are also GNU extensions, but they have since been copied to a few other implementations. If your implementation is one of the few where that still don't have them, you can replace:

  • find . -print0 with find . -exec printf '%s\0' {} +
  • find . -mindepth 1 -printf '%P\0' with find . ! -name . -exec sh -c 'for i do printf "%s\0" "${i#./}"; done' sh {} +
0

You can do it by piping the output to awk:

find . | awk '/ /{ printf"\"%s\"\n", $0; next } { print }' 
bdowling
  • 136
  • 4