46

I would like to output the list of items in a folder in the folowing way:

"filename1"  "filename2" "file name with spaces" "foldername" "folder name with spaces"

In other words, item names must be in a single line, surrounded with quotes (single or double) and divided by spaces.

I know that

find . | xargs echo

prints output in a single line, but I do not know how to add quotes around each item name.

This code is part of a bsh script. The solution can therefore be a set of commands and use temporary files for storing intermediate output.

Thank you very much for any suggestion.

Cheers, Ana

Sebastian Paaske Tørholm
  • 49,493
  • 11
  • 100
  • 118
Ana
  • 463
  • 1
  • 4
  • 4
  • 1
    If the goal is to pass names with spaces to xargs, literal quotes are the wrong way to do it. – Charles Duffy Dec 14 '15 at 22:21
  • Indeed, I just spent too long researching this and what I wanted, to pass found output to `rm` removing files older than a year, was: `find ./backups -mindepth 1 -maxdepth 1 -type f -mtime +365 -print0 | xargs -0 -r rm -f`. – Jez Apr 24 '23 at 15:09

11 Answers11

59

You could also simply use find "-printf", as in :

find . -printf "\"%p\" " | xargs your_command

where:

%p = file-path

This will surround every found file-path with quotes and separate each item with a space. This avoids the use of multiple commands.

Mark McClelland
  • 4,980
  • 4
  • 35
  • 48
Benjamin A.
  • 899
  • 1
  • 8
  • 6
33

this should work

find $PWD | sed 's/^/"/g' | sed 's/$/"/g' | tr '\n' ' '

EDIT:

This should be more efficient than the previous one.

find $PWD | sed -e 's/^/"/g' -e 's/$/"/g' | tr '\n' ' '

@Timofey's solution would work with a tr in the end, and should be the most efficient.

find $PWD -exec echo -n '"{}" ' \; | tr '\n' ' '
freethinker
  • 1,286
  • 1
  • 10
  • 17
  • This works great! Thank you very much. My final command is now: "find . ! -type l \( -name . -o -prune \) | sort -n | sed 's/^/"/g' | sed 's/$/"/g' | tr '\n' ' ' | xargs myCommand". This single line was needed as input parameter for myCommand. – Ana May 18 '11 at 08:29
  • 2
    Please, please no. First, `find $PWD` will behave badly if your `$PWD` contains spaces or literal glob characters that expand to anything. Second, substituting double quotes around a name to try to escape it is a weak and easily circumvented mechanism -- valid filenames can themselves contain `"` or `'` characters, thus throwing off parsing for the entire rest of the stream. – Charles Duffy Dec 14 '15 at 22:18
  • @Ana, if on a system with GNU tools (`find -print0` and `sort -z`), it's far better to use `find ... -print0 | sort -n -z | xargs -0 myCommand` – Charles Duffy Dec 14 '15 at 22:19
  • @Ana, ...even if you don't want to use NUL delimiters, you can still tell `xargs` to use only newline delimiters and be safe against filenames with spaces (albeit not filenames with newlines, but the answer you've already accepted is also already unsafe against filenames containing newline literals): `find ... | sort -n | xargs -d $'\n' myCommand` – Charles Duffy Dec 14 '15 at 22:20
  • You should really use "ls" to list directory contents. It DO have the needed options. – AnrDaemon May 26 '16 at 20:28
  • But how to give this output with quoted to XARGS input for another command execution ? – Kanagavelu Sugumar Dec 19 '18 at 08:15
20

You can use the GNU ls option --quoting-style to easily get what you are after. From the manual page:

--quoting-style=WORD

use quoting style WORD for entry names: literal, locale, shell, shell-always, shell-escape, shell-escape-always, c, escape

For example, using the command ls --quoting-style=shell-escape-always, your output becomes:

'filename1' 'filename2' 'file name with spaces' 'foldername' 'folder name with spaces'

Using --quoting-style=c, you can reproduce your desired example exactly. However, if the output is going to be used by a shell script, you should use one of the forms that correctly escapes special characters, such as shell-escape-always.

Community
  • 1
  • 1
George Hilliard
  • 15,402
  • 9
  • 58
  • 96
20

Try this.

find . -exec echo -n '"{}" ' \;
Timofey Stolbov
  • 4,501
  • 3
  • 40
  • 45
10

After 10 years, no one suggested the Bash "|while read" method?

find * -type d -depth 0|while read f; do echo \"$f\"; done

It's a simple Bash shell pipeline instead of launching another program like sed or xarg. If you really want to do something with each file/folder:

find * -type d -depth 0|while read f; do du -sh "$f"; done

By the way, find * uses another Bash feature that excludes .xyz files/folders and will not output the ./ prefix find . does.

Michael Chen
  • 631
  • 5
  • 12
5
for f in *; do printf "'%s' " "$f"; done; echo

Or, thanks to Gordon Davisson:

printf "'%s' " *; echo

The trailing echo is simply to add a newline to the output.

Community
  • 1
  • 1
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
1

EDIT:
The following answer generate a new-line separated LIST instead of a single line.

  1. I'm second guessing that the OP uses the result for invoking other command
  2. converting the output LIST to single line is easy (| tr '\n' ' ')

A less mentioned method is to use -d (--delimiter) option of xargs:

find . | xargs -I@ -d"\n" echo \"@\" 

-I@ captures each find result as @ and then we echo-ed it with quotes

With this you can invoke any commands just as you added quotes to the arguments.

$ find . | xargs -d"\n" testcli.js
[ "filename1",
  "filename2",
  "file name with spaces",
  "foldername",
  "folder name with spaces" ]

See https://stackoverflow.com/a/33528111/665507

Community
  • 1
  • 1
leesei
  • 6,020
  • 2
  • 29
  • 51
1

try

ls | sed -e 's/^/"/g' -e 's/$/"/g' | tr '\n' ' '
Ferroao
  • 3,042
  • 28
  • 53
1
    find . | sed "s|.*|\"&\"|"

Brief description:
We take result of .* pattern and put it into quotes.
Good source is sed.

Detail description:
Pattern: s/one/ONE/

  • s Substitute command.
  • / Delimiter.
    In my expression "|" is used instead of "/" to prevent "mountains" like in pattern s/.*/\"&\"/.
  • one Regular expression pattern search pattern.
  • ONE Replacement string.
  • .* Means any symbol that repeats unlimited number of times.
  • \" Means " itself.
  • & Special character that corresponds to the pattern found.
0

Try this...

find . -print0 | xargs -0 echo

the -print0 argument makes the output null-terminated instead of a new-line, and the -0 on xargs says to use null as the delimiter. As such, spaces are preserved as you would expect without the need to quote things.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
-1

To avoid hacks trying to manually add quotes, you can take advantage of printfs %q formatting option:

❯ ll .zshrc.d
total 112K
-rwxrwxrwx 1 root root  378 Jul  1 04:39  options.zsh*
-rwxrwxrwx 1 root root   57 Jul  1 04:39  history.zsh*
-rwxrwxrwx 1 root root  301 Jul  1 05:01  zinit.zsh*
-rwxrwxrwx 1 root root  79K Jul  1 05:19  p10k.zsh*
-rwxrwxrwx 1 root root  345 Jul  1 05:24  zplugins.zsh*
-rwxrwxrwx 1 root root  490 Jul  4 23:40  aliases.zsh*
-rw-r--r-- 1 root root 9.0K Jul 27 08:14  lscolors.zsh
-rw-r--r-- 1 root root    0 Aug 30 05:56 'foo bar'
-rw-r--r-- 1 root root    0 Aug 30 05:58 '"foo"'

❯ find .zshrc.d -exec printf '%q ' {} +
.zshrc.d .zshrc.d/history.zsh .zshrc.d/aliases.zsh .zshrc.d/zplugins.zsh .zshrc.d/p10k.zsh .zshrc.d/zinit.zsh .zshrc.d/options.zsh '.zshrc.d/"foo"' '.zshrc.d/foo bar' .zshrc.d/lscolors.zsh #

vs:

find .zshrc.d -printf "\"%p\" "
".zshrc.d" ".zshrc.d/history.zsh" ".zshrc.d/aliases.zsh" ".zshrc.d/zplugins.zsh" ".zshrc.d/p10k.zsh" ".zshrc.d/zinit.zsh" ".zshrc.d/options.zsh" ".zshrc.d/"foo"" ".zshrc.d/foo bar" ".zshrc.d/lscolors.zsh" #

Notice the file .zshrc.d/"foo" is incorrectly escaped.

❯ echo ".zshrc.d/"foo""
.zshrc.d/foo

❯ echo '.zshrc.d/"foo"'
.zshrc.d/"foo"
mpen
  • 272,448
  • 266
  • 850
  • 1,236