代码之家  ›  专栏  ›  技术社区  ›  Juan A. Navarro

如何在sh中有一个字符串中的换行符?

sh
  •  252
  • Juan A. Navarro  · 技术社区  · 14 年前

    这个

    STR="Hello\nWorld"
    echo $STR
    

    作为输出生成

    Hello\nWorld
    

    而不是

    Hello
    World
    

    这个问题不是关于 回声 . echo -e ,但我正在寻找一种解决方案,允许将字符串(包括换行符)作为参数传递给 其他 没有类似解释选项的命令 \n 作为新线。

    10 回复  |  直到 9 年前
        1
  •  404
  •   amphetamachine    4 年前

    $'string' ,例如:

    $ STR=$'Hello\nWorld'
    $ echo "$STR" # quotes are required here!
    Hello
    World
    

    $ STR='Hello
    > World'
    

    Bash很不错。它接受的不仅仅是 \n $'' 字符串。下面是Bash手册页面的摘录:

       Words of the form $'string' are treated specially.  The word expands to
       string, with backslash-escaped characters replaced as specified by  the
       ANSI  C  standard.  Backslash escape sequences, if present, are decoded
       as follows:
              \a     alert (bell)
              \b     backspace
              \e
              \E     an escape character
              \f     form feed
              \n     new line
              \r     carriage return
              \t     horizontal tab
              \v     vertical tab
              \\     backslash
              \'     single quote
              \"     double quote
              \nnn   the eight-bit character whose value is  the  octal  value
                     nnn (one to three digits)
              \xHH   the  eight-bit  character  whose value is the hexadecimal
                     value HH (one or two hex digits)
              \cx    a control-x character
    
       The expanded result is single-quoted, as if the  dollar  sign  had  not
       been present.
    
       A double-quoted string preceded by a dollar sign ($"string") will cause
       the string to be translated according to the current  locale.   If  the
       current  locale  is  C  or  POSIX,  the dollar sign is ignored.  If the
       string is translated and replaced, the replacement is double-quoted.
    
        2
  •  180
  •   Jens    3 年前

    Echo是如此的九十年代,充满了危险,它的使用应该会导致核心转储不低于4GB。说真的,echo的问题是Unix标准化过程最终发明了 printf

    因此,要在字符串中获得换行符,有两种方法:

    # 1) Literal newline in an assignment.
    FOO="hello
    world"
    # 2) Command substitution.
    BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
    printf '<%s>\n' "$FOO"
    printf '<%s>\n' "$BAR"
    

    现在不要回头。

        3
  •  66
  •   zvezda    12 年前

    根据其他答案我所做的是

    NEWLINE=$'\n'
    my_var="__between eggs and bacon__"
    echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"
    
    # which outputs:
    spam
    eggs__between eggs and bacon__bacon
    knight
    
        4
  •  31
  •   tripleee    5 年前

    我找到了 -e 旗子飘逸挺拔

    bash$ STR="Hello\nWorld"
    
    bash$ echo -e $STR
    Hello
    World
    

    indexes_diff=$(git diff index.yaml)
    echo "$indexes_diff"
    
        5
  •  30
  •   tripleee    9 年前

    问题不在外壳上。问题其实出在 echo echo -e 但这并不是所有平台都支持的原因之一 printf 为了便于携带,现在建议使用。

    您还可以尝试将换行符直接插入到shell脚本中(如果您正在编写的是一个脚本),使其看起来像。。。

    #!/bin/sh
    echo "Hello
    World"
    #EOF
    

    或同等地

    #!/bin/sh
    string="Hello
    World"
    echo "$string"  # note double quotes!
    
        6
  •  21
  •   user2350426 user2350426    8 年前
    1. $ STR='new
      line'
      $ printf '%s' "$STR"
      new
      line
      

      是的,那意味着写作 代码中需要的地方。

    2. 有几个等价于 new line 性格。

      \n           ### A common way to represent a new line character.
      \012         ### Octal value of a new line character.
      \x0A         ### Hexadecimal value of a new line character.
      

      但所有这些都需要某种工具的“解释”( POSIX printf

      echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
      printf 'new\nline'            ### Understood by POSIX printf.
      printf 'new\012line'          ### Valid in POSIX printf.
      printf 'new\x0Aline'       
      printf '%b' 'new\0012line'    ### Valid in POSIX printf.
      

      因此,需要该工具来构建具有新行的字符串:

      $ STR="$(printf 'new\nline')"
      $ printf '%s' "$STR"
      new
      line
      
    3. 在某些壳中,序列 ' 是一种特殊的外壳扩展。

      $ STR=$'new\nline'
      
    4. 当然,更复杂的解决方案也是可能的:

      $ echo '6e65770a6c696e650a' | xxd -p -r
      new
      line
      

      或者

      $ echo "new line" | sed 's/ \+/\n/g'
      new
      line
      
        7
  •  8
  •   caot    7 年前

    $ echo $'Hello\nWorld'
    Hello
    World
    $ echo $"Hello\nWorld"
    Hello\nWorld
    
        8
  •  4
  •   Jason    8 年前

    我不是bash专家,但这个对我很有用:

    STR1="Hello"
    STR2="World"
    NEWSTR=$(cat << EOF
    $STR1
    
    $STR2
    EOF
    )
    echo "$NEWSTR"
    

    我发现这样更容易格式化文本。

        9
  •  2
  •   Jelle Geerts    4 年前

    免责声明:我第一次写这个,然后偶然发现这个问题。我以为这个解决方案还没有发布,看到TLC也发布了类似的答案。不过,我张贴这是因为我希望这是一个有用的和彻底的解释。

    这似乎是一个可移植的解决方案,因为它可以在相当多的shell上工作(参见注释)。
    这样你可以得到一个真正的换行到一个变量。

    此解决方案的好处是,您不必在源代码中使用换行符,因此可以缩进 你的代码以任何你想要的方式,解决方案仍然有效。这使得它很健壮。它也是便携式的。

    # Robust way to put a real newline in a variable (bash, dash, ksh, zsh; indentation-resistant).
    nl="$(printf '\nq')"
    nl=${nl%q}
    

    长答案:

    上述解决方案说明:

    我们可以证明变量包含实际的换行符(0x0A):

    printf '%s' "$nl" | hexdump -C
    00000000  0a  |.|
    00000001
    

    '%s' '\n'

    当然,除了这个答案中提出的解决方案,我们还可以使用这个(但是……):

    nl='
    '
    

    ... 但是,这种方法的健壮性较差,并且很容易因为意外地缩进代码或之后忘记删除代码而受到损坏,这使得在(缩进的)函数中使用变得不方便,而早期的解决方案是健壮的。

    至于双引号:
    双引号的原因 " 围绕命令替换,如 nl="$(printf '\nq')" 你甚至可以在变量赋值前加上 local dash shell会有麻烦,因为dash会丢失'q',最后会得到一个空的'nl'变量(同样,由于命令替换)。
    另一个例子更好地说明了这一问题:

    dash_trouble_example() {
        e=$(echo hello world) # Not using 'local'.
        echo "$e" # Fine. Outputs 'hello world' in all shells.
    
        local e=$(echo hello world) # But now, when using 'local' without double quotes ...:
        echo "$e" # ... oops, outputs just 'hello' in dash,
                  # ... but 'hello world' in bash and zsh.
    
        local f="$(echo hello world)" # Finally, using 'local' and surrounding with double quotes.
        echo "$f" # Solved. Outputs 'hello world' in dash, zsh, and bash.
    
        # So back to our newline example, if we want to use 'local', we need
        # double quotes to surround the command substitution:
        # (If we didn't use double quotes here, then in dash the 'nl' variable
        # would be empty.)
        local nl="$(printf '\nq')"
        nl=${nl%q}
    }
    

    上述解决方案的实例:

    # Parsing lines in a for loop by setting IFS to a real newline character:
    
    nl="$(printf '\nq')"
    nl=${nl%q}
    
    IFS=$nl
    
    for i in $(printf '%b' 'this is line 1\nthis is line 2'); do
        echo "i=$i"
    done
    
    # Desired output:
    # i=this is line 1
    # i=this is line 2
    
    # Exercise:
    # Try running this example without the IFS=$nl assignment, and predict the outcome.
    
        10
  •  0
  •   Alexey alexvassel    7 年前

    在我的系统(Ubuntu17.10)上,你的例子可以正常工作,无论是从命令行(into)输入 sh )当被执行死刑时

    [bash]§ sh
    $ STR="Hello\nWorld"
    $ echo $STR
    Hello
    World
    $ exit
    [bash]§ echo "STR=\"Hello\nWorld\"
    > echo \$STR" > test-str.sh
    [bash]§ cat test-str.sh 
    STR="Hello\nWorld"
    echo $STR
    [bash]§ sh test-str.sh 
    Hello
    World
    

    我想这回答了你的问题:它只是工作(我还没有试图弄清楚细节,比如换行符到底是在什么时候替换的 \n 发生在 ).

    然而,我注意到同样的脚本在执行时会有不同的行为 具有 bash 然后打印出来 Hello\nWorld

    [bash]§ bash test-str.sh
    Hello\nWorld
    

    猛击

    [bash]§ STR="Hello
    > World"
    [bash]§ echo "$STR"
    

    注意两边的双引号 $STR 猛击 脚本。

    下面还提供了所需的输出:

    [bash]§ echo "Hello
    > World"
    
        11
  •  0
  •   tlwhitec    6 年前

    那些吹毛求疵的人只需要换行,而轻视打破缩进的多行代码,他们可以做到:

    IFS="$(printf '\nx')"
    IFS="${IFS%x}"
    

    Bash(很可能还有其他shell)在命令替换之后会吞噬掉所有尾随的换行符,因此需要结束 printf

    IFS="$(printf '\nx')" IFS="${IFS%x}"
    

    我知道这是两个动作而不是一个动作,但是我的缩进和可移植性OCD现在很平静:)我最初开发这个功能是为了能够拆分换行符,只拆分单独的输出,最后我使用了 \r 作为终止字符。这使得换行分裂即使对于以 \r\n .

    IFS="$(printf '\n\r')"
    
        12
  •  0
  •   Adam K Dean    5 年前

    str=$(printf "%s" "first line")
    str=$(printf "$str\n%s" "another line")
    str=$(printf "$str\n%s" "and another line")
    
        13
  •  0
  •   Karthik Suresh    4 年前

    这并不理想,但我编写了大量代码,并以类似于问题中使用的方法的方式定义了字符串。被接受的解决方案需要我重构大量的代码,所以相反,我替换了 \n 具有 "$'\n'" 这对我有用。