Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Shell 为什么'';对于“中的项目”$列表“'';只迭代一次?_Shell_Loops_Posix - Fatal编程技术网

Shell 为什么'';对于“中的项目”$列表“'';只迭代一次?

Shell 为什么'';对于“中的项目”$列表“'';只迭代一次?,shell,loops,posix,Shell,Loops,Posix,我有一个简单的代码: #~/bin/sh partitions="$(ls -1 /dev/loop?* | tr '\n' ' ')" for partition in ${partitions} do echo "A loop partition: ${partition}" done 有输出 A loop partition: /dev/loop0 A loop partition: /dev/loop1 A loop partition: /dev/loop2 A loop pa

我有一个简单的代码:

#~/bin/sh

partitions="$(ls -1 /dev/loop?* | tr '\n' ' ')"
for partition in ${partitions}
do
  echo "A loop partition: ${partition}"
done
有输出

A loop partition: /dev/loop0
A loop partition: /dev/loop1
A loop partition: /dev/loop2
A loop partition: /dev/loop3
A loop partition: /dev/loop4
A loop partition: /dev/loop5
A loop partition: /dev/loop6
A loop partition: /dev/loop7
A loop partition: /dev/loop-control
当我在
for
循环中的
${partitions}
变量周围添加引号时(使其成为
“${partitions}”
),输出将不同

加上引号:

#~/bin/sh

partitions="$(ls -1 /dev/loop?* | tr '\n' ' ')"
for partition in "${partitions}"
do
  echo "A loop partition: ${partition}"
done
新产出:

A loop partition: /dev/loop0 /dev/loop1 /dev/loop2 /dev/loop3 /dev/loop4 /dev/loop5 /dev/loop6 /dev/loop7 /dev/loop-control 
为什么引号会以这种方式影响输出?


有人告诉我,在shell脚本中把
${vars}
放在引号中是一种很好的做法。这实际上是一种不好的做法吗?

在变量周围加引号会使其值变成一个单词,即使它包含空格。但是,
for
会迭代变量中的单词。您已经显式地创建了一个变量,该变量用空格分隔成单词,然后通过添加引号来关闭单词拆分。

在变量周围加引号会使其值变成单个单词,即使它包含空格。但是,
for
会迭代变量中的单词。您已显式创建了一个按空格分隔为单词的变量,然后通过添加引号来关闭单词拆分。

使用引号可防止字符串拆分(将包含单词的项目拆分为多个片段)和全局扩展(将每个片段视为可能匹配多个文件名的模式)

但是,原始代码依赖于字符串拆分,因为它将
分区
存储为单个字符串。不要这样做:将列表存储在数组变量中,这样即使在抑制字符串拆分的情况下,它们也会保留单独的标识

shopt -s nullglob                  # cause a glob with no matches to not generate any items
partitions=( /dev/loop?* )         # generate our list of partitions *as an array*
for partition in "${partitions[@]}"; do       # ...then iterate over "${array[@]}"
  echo "A loop partition: ${partition}"
done

上面的方法更有效(它只使用shell本身内置的工具,而不是ls),而且更正确(它不会将带有空格的文件名拆分为单独的单词,或者在存在其他文件名时用其他文件名替换名称中的glob)。

使用引号可以防止字符串拆分(将包含单词的项拆分为多个部分)和全局扩展(将每个部分视为可能匹配多个文件名的模式)

但是,原始代码依赖于字符串拆分,因为它将
分区
存储为单个字符串。不要这样做:将列表存储在数组变量中,这样即使字符串拆分被抑制,它们也会保留单独的标识

shopt -s nullglob                  # cause a glob with no matches to not generate any items
partitions=( /dev/loop?* )         # generate our list of partitions *as an array*
for partition in "${partitions[@]}"; do       # ...then iterate over "${array[@]}"
  echo "A loop partition: ${partition}"
done

上面的方法更有效(它只使用shell本身内置的工具,而不是
ls
),而且更正确(它不会将带有空格的文件名拆分为单独的字,或者在存在其他文件名时,用其他文件名替换名称中的glob).

这是针对这个问题的。要清楚,在字符串变量中存储列表是不好的做法。如果您的变量是数组,
用于“${partitions[@]}”中的分区
将是迭代它的正确方法。另请参见和。为什么在变量中有一个列表和在(比如)文件中有一个列表之间甚至有区别?另外@CharlesDuffy我尝试在脚本中保持POSIX的一致性;我倾向于远离bash,数组仅限于bash(虽然在ksh中也有技术支持,但…).@Wimateeka,…当你说“比如说,文件中的列表”--列表中条目的文件是如何分隔的?如果您对每个项使用一行,这对文件名来说是不安全的--文件名可以包含文字换行符。如果您不打算转义或编码C字符串列表,则在文件中存储C字符串列表的唯一安全方法是使用NUL分隔项…也就是说,
用于/dev/loop中的分区?*;do
可能是解决当前问题的最快的POSIX兼容方式,它不涉及字符串拆分。类似地,
find…-exec sh-c'for arg;用“$arg”做点什么;done'{}+
提供了一种POSIX兼容方式来迭代
find
不依赖于无引号扩展的输出(注意
{}
不在解析为代码的字符串范围内;这很关键)。这是解决此问题的方法。需要明确的是,在字符串变量中存储列表是不好的做法。如果变量是数组,则在“${partitions[@]}”中为分区指定
将是迭代它的正确方法。另请参见和。为什么在变量中有一个列表和在(比如)文件中有一个列表之间甚至有区别?另外@CharlesDuffy我尝试在脚本中保持POSIX的一致性;我倾向于远离bash,数组仅限于bash(虽然在ksh中也有技术支持,但…).@Wimateeka,…当你说“比如说,文件中的列表”--列表中条目的文件是如何分隔的?如果您对每个项使用一行,这对文件名来说是不安全的--文件名可以包含文字换行符。如果您不打算转义或编码C字符串列表,则在文件中存储C字符串列表的唯一安全方法是使用NUL分隔项…也就是说,
用于/dev/loop中的分区?*;do
可能是解决当前问题的最快的POSIX兼容方式,它不涉及字符串拆分。类似地,
find…-exec sh-c'for arg;用“$arg”做点什么;done'{}+
提供了一种POSIX兼容方式来迭代
find
不依赖于无引号扩展的输出(注意
{}
在解析为代码的字符串之外;这很关键)。如果不使用数组,解决这个问题的方法是什么?
对于/dev/loop*中的分区;
还有其他一些方法——f/e,您可以使用函数来存储全局,如
使用分区(){“$@”/dev/loop?*}
,然后使用_partitionsprintf'一个循环分区:%s\n'
,但这种事情通常是相当愚蠢的。有一点是,在基线POSIX中编写正确的健壮性所需的所有黑客花费的时间/精力/可读性比瞄准基线POSIX sh给你带来的好处要多。解决这个问题的方法是什么在/dev/loop*中分区时不使用数组?
;do
还有其他一些方法——f/e,您可以使用函数来存储glo