Daniel
Daniel

Reputation: 1696

What did echo and quotes do?

nkcoder@nkcoder:bash$ ls
doublebracket.sh  for.sh  if.sh  quote.sh  singlebracket.sh  test.sh
nkcoder@nkcoder:bash$ ls | wc -l
6
nkcoder@nkcoder:bash$ echo $(ls)
doublebracket.sh for.sh if.sh quote.sh singlebracket.sh test.sh
nkcoder@nkcoder:bash$ echo $(ls) | wc -l
1
nkcoder@nkcoder:bash$ echo "$(ls)"
doublebracket.sh
for.sh
if.sh
quote.sh
singlebracket.sh
test.sh
nkcoder@nkcoder:bash$ echo "$(ls)" | wc -l
6
  1. The output of ls is 6 lines, where is the newline ?
  2. why quotes affects the result when using "$(ls)" ?

I'm really confused, could anybody help to provide some explanation? Thanks a lot.

Upvotes: 0

Views: 89

Answers (3)

Charles Duffy
Charles Duffy

Reputation: 295766

As I read it, there are multiple questions here:


Why does ls | wc -l report a different number of lines of output than that seen running ls manually?

ls checks whether its stdout is a TTY -- the same check your own scripts can do with [[ -t 1 ]]. If that test returns true, it modifies its output to be one line per file by default.


Why does echo $(ls) differ from echo "$(ls)"?

The output from expansions -- such as $(ls) or $foo is word-split if the expansion was not quoted.

Word-splitting happens on characters in the shell variable IFS, or -- by default -- the space character, the tab, and the newline.

Results from that splitting are then substituted as individual arguments to the command being run. So:

$ rm -rf tmp.d && mkdir -p tmp.d && cd tmp.d && touch "hello cruel" world
$ echo $(ls)

...will first run ls. Because output is not to a TTY, the output from ls looks like this:

hello cruel
world

However, because the expansion was not inside quotes, the shell then splits that block into words. Because both the newline and the space character are in IFS by default, the fact that hello cruel is a single filename is lost. Thus, we run:

$ echo "hello" "cruel" "world"

By contrast, if you run:

$ echo "$(ls)"

...then the shell doesn't string-split on whitespace, so it invokes:

$ echo 'hello cruel
world'

Mind you, if you really want to iterate over files, you shouldn't be using ls at all. See Why you shouldn't parse the output of ls(1)

Upvotes: 1

Sriharsha Kalluru
Sriharsha Kalluru

Reputation: 1823

Double quotes will preserve the spaces and new lines of ls command to echo command and that is the reason when you are using double quotes you are getting actual count rather than using with out quotes.

# echo hai\
> bye
haibye
# echo "hai
> bye"
hai
bye

Upvotes: 0

piojo
piojo

Reputation: 6723

In bash, all unquoted arguments (including an argument generated by command substitution (`` or $())) is combined into a single line. Using quotes keeps the argument (the command substitution output) in its original form, so the newlines are preserved.

To see the difference, you can run:

function three_lines()
{
    echo one; echo two; echo three
}
$ three_lines
one
two
three
$ echo `three_lines`
one two three
$ echo "`three_lines`"
one
two
three

Upvotes: 2

Related Questions