在 bash 脚本中循环固定字符串或变量字符串

eth*_*ion 1 bash

我是 bash 脚本的新手,有两个循环如下:

#!/bin/bash

##########
# Loop 1 #
##########
for x in "one two three four";
do
    echo $x
done
Run Code Online (Sandbox Code Playgroud)

输出:

one two three four
Run Code Online (Sandbox Code Playgroud)
##########
# Loop 2 #
##########
var="one two three four"
for x in $var;
do
    echo $x
done
Run Code Online (Sandbox Code Playgroud)

输出:

one
two
three
four
Run Code Online (Sandbox Code Playgroud)

我正在寻找指针来帮助理解为什么第一个循环echo是字符串,而第二个循环在空格处拆分,然后echo是每个拆分的单词。

编辑:为了清楚起见,重新表述了问题。

Cha*_*ffy 8

这里的基本误解如下:shell遍历字符串;它只循环遍历它所谓的“单词”列表。当您执行不带引号的扩展(如$var)时,除非它在隐式抑制字符串拆分(也称为“分词”)的上下文中,否则 的内容$var将在IFS(默认情况下:制表符、空格和换行符)中的字符上拆分以形成一个单词列表,然后每个单词都扩展为一个 glob(因此*.txt可以替换为a.txt b.txt c.txt-- 或者file one.txt,就此而言,如果当前目录中存在包含空格的匹配名称)。

In for x in "one two three four", one two three four-- 因为它被引用了 -- 是一个词。相比之下,for x in one two three fourorfor x in "one" "two" "three" "four"将迭代每个one, two, three, 和four作为一个不同的词。

当你运行var="one two three four"; for x in $var,然后$var是 - 因为扩展没有引用 - 分成多个词,所以one是一个词,two第二个,three第三个,four第四个。

另一方面,如果您运行for x in "$var",引号将抑制字符串拆分和通配符,因此循环内部只会运行一次,整个字符串保持在一起。


最好的做法是根本不使用for x in $var。当您想在 bash 中拥有一个事物列表时,请使用数组来存储该列表。

#!/usr/bin/env bash
#              ^^^^ - NOT sh, which doesn't support arrays

var=( "first word" "second word" )
for x in "${var[@]}"; do
  echo "$x"
done
Run Code Online (Sandbox Code Playgroud)

  • @ethelion,...当您将字符串取消引用为数组时,不会自动发生分词;它只发生在上下文中未加引号的扩展期间,它没有被其他方式抑制,并且字符串赋值的右侧是一个被抑制的上下文。 (2认同)
  • @ethelion,...现在,如果您 _did_ 想使用字符串拆分将您的字符串分解成一个数组,那就是 `array=( $var )`,然后是 `echo "${array[1]}" ` 确实会输出 `two`(因为第一项在索引 0 中,而不是索引 1)。 (2认同)
  • @ethelion,...也就是说,`array=( $var )` 通常被认为是一种反模式——参见 [BashPitfalls #50](https://mywiki.wooledge.org/BashPitfalls#hosts.3D.28_.24.28 aws_....29_.29)。通常`read -r -a array <<<"$var"` 是更好的选择,因为它抑制了 globbing 并且 _only_ 在 IFS 中对字符进行拆分;或者 `readarray -t array <<<"$var"` 如果你只想在换行符上拆分.. (2认同)