Bash semicolon being equal to newline is not exactly true?

强颜欢笑 提交于 2021-02-16 14:12:25

问题


I've read in multiple articles that semicolon(;) in UNIX-like shells is equal to a new line.
However, the following is confusing me and I'm having no luck googling it either.

I'm guessing it's an issue with do in shell, but "bash semicolon do" is not exactly the most google-friendly search term combination.

Below is a simple for statement.

for i in {1..10}
do
echo "hi"
echo "bye"
done

As many Stack Overflow gurus have posted, every newline can be substituted with semicolons.

So.. we have this following "same" statement.

for i in {1..10}; do; echo "hi"; echo "bye"; done

and we get:

-bash: syntax error near unexpected token `;'

What exactly is the semicolon? Is this just an unique issue with do?


回答1:


Looking at the syntax of the for/do loop,

for name [ [in [words …] ] ; ] do commands; done

we can see that do is followed by commands immediately, so using a newline after do doesn't replace a semicolon, but a space.

The description for compound commands also says

In most cases a list of commands in a compound command’s description may be separated from the rest of the command by one or more newlines, and may be followed by a newline in place of a semicolon.

but nowhere does it say that you can insert random semicolons. "Every newline can be substituted with semicolons" is simply too general a statement and not correct.

More manual evidence: in the section about lists of commands, it says (emphasis mine):

A list is a sequence of one or more pipelines separated by one of the operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or a newline.

Of these list operators, && and || have equal precedence, followed by ; and &, which have equal precedence.

A sequence of one or more newlines may appear in a list to delimit commands, equivalent to a semicolon.

So a newline is equivalent to a semicolon within a list of commands.




回答2:


It's not just with do; there are a number of contexts where the shell allows a newline where it would not allow a semicolon.

Here are most of them (leaving out the uses of newlines or semicolons inside quotes, which are always distinct from each other):

  1. After && or ||

    some_command &&
    some_other_command
    
  2. After a pipe (|):

    some_producer |
    some_consumer
    
  3. Before the in in a for statement:

    for x
    in $(produce_values); do
    

    Although empty commands are illegal, the shell grammar does allow empty value lists in a for command, so for x in; do do_something; done is legal, and so would be the same thing written with newlines instead of semicolons.

  4. Before or after the in in a case statement; also after the closing ) of each pattern and the ;; which closes each case:

    case $x
    in
    pattern)
      do_something ;;
    esac
    
  5. After the keywords if, then, elif, else, while or until:

    if
      some_condition
    then
      do_something
    else
      do_something_else
    fi
    
  6. After the { or ( which opens a compound command or subshell:

    {
       a_command
       another_command
    }
    
    (
       a_command
       another_command
    )
    

    Similarly, after the $( which starts a command substitution:

    a=$(
      echo hello
    )
    

    In bash, this also applies to the ( in process substitution: <(...). See below for the slightly different handling in the bash extensions dealing with conditionals and arithmetic substitution.

  7. After the ) in a function definition:

    my_function()
    {
      do_something
    }
    
  8. After the ; or & which terminates a command:

    do_something;
    do_something_in_background &
    

    (Note that empty commands are illegal, so do_something; ; would be rejected.)

I took that list from the shell grammar in the Posix standard. Bash allows newlines in other places; I couldn't think of any when I wrote this answer, but @BenjaminW reminded me:

  • Inside a parenthesized array literal, either in an array assignment or an array declaration, newlines are considered whitespace:

    a+=(
      first_thing
      second_thing
    )
    
    local -A b=(
      [x]="value of x"
      [y]="value of y"
    )
    

And then I remembered these other pseudo-quoted environments:

  • Bash also accepts newlines as whitespace inside arithmetic expressions:

    i=$((
       i + 
         3 * j
    ))
    
    if ((
      i + 3 * j
      >
      9
    ))
    

    and after the [[ or before the ]] in [[-style conditional statements:

    if
    [[
          $a -eq "done"
    ]]    
    then break
    fi
    


来源:https://stackoverflow.com/questions/51034076/bash-semicolon-being-equal-to-newline-is-not-exactly-true

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!