4

How can I print a list of files/directories one-per-line using echo?

I can replace spaces with newlines, but this doesn't work if the filenames contain spaces:

$ echo small*jpg
small1.jpg small2.jpg small photo 1.jpg small photo 2.jpg

$ echo small*jpg | tr ' ' '\n'
small1.jpg
small2.jpg
small
photo
1.jpg
small
photo
2.jpg

I know I can do this with ls -d1, but is it also possible using echo?

13

echo can't be used to output arbitrary data anyway, use printf instead which is the POSIX replacement for the broken echo utility to output text.

printf '%s\n' small*jpg

You can also do:

printf '%s\0' small*jpg

to output the list in NUL delimited records (so it can be post-processed; for instance using GNU xargs -r0; remember that the newline character is as valid as space or any character in a filename).

Before POSIX came up with printf, ksh already had a print utility to replace echo. zsh copied it and added a -l option to print the arguments one per line:

print -rl -- small*jpg

ksh93 added a -f option to print for printf like printing. Copied by zsh as well, but not other ksh implementations:

print -f '%s\n' -- small*jpg

Note that all of those still print an empty line if not given any argument. A better println can be written as a function as:

println() {
  [ "$#" -eq 0 ] || printf '%s\n' "$@"
}
3

echo only uses spaces to separate the strings it receives as arguments.

Since your question is tagged , here is what help echo in bash says (emphasis mine):

Display the ARGs, separated by a single space character and followed by a newline, on the standard output.

Similar statements are found in the documentation for other implementations. E.g. that of echo from coreutils, which you would likely find on GNU/Linux:

echo writes each given string to standard output, with a space between each and a newline after the last one.

If you really want echo to print your file names on separate lines you have to feed them as a single string:

$ touch "small1.jpg" "small2.jpg" "small photo 1.jpg" "small photo 2.jpg"
$ (set -- small*.jpg; IFS='
'; echo "$*")
small1.jpg
small2.jpg
small photo 1.jpg
small photo 2.jpg

Here we are leveraging the behavior of the * special parameter: within double-quotes it expands to a single word in which the elements of the array of positional parameters are concatenated using the first character of the IFS variable (which we set to a newline).

The (...) syntax is used to execute the commands in a subshell environment—we generally don't want to affect the main one.

Note, however, that all echo's limitations still apply, as mentioned in Stéphane's answer, and therefore its use in this scenario is not advisable.

  • I strongly recommend setting IFS back to normal after anything like this. – Gordon Davisson Jun 18 at 1:18
  • @GordonDavisson Right, thank you. Answer amended. – fra-san Jun 18 at 6:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.