it-swarm.cn

如何从bash中的变量逐行读取?

我有一个变量,其中包含命令的多行输出。从变量逐行读取输出的最有效方法是什么?

例如:

jobs="$(jobs)"
if [ "$jobs" ]; then
    # read lines from $jobs
fi
53
Eugene Yarmash

您可以使用while循环进行进程替换:

while read -r line
do
    echo "$line"
done < <(jobs)

读取多行变量的最佳方法是设置空白IFS变量和printf变量,并在其后添加换行符:

# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
   echo "$line"
done < <(printf '%s\n' "$var")

注意:根据 shellcheck sc2031 ,使用进程替代比使用管道更可取,以避免[巧妙地]创建子shell。

另外,请注意,通过命名变量jobs可能会引起混淆,因为这也是常见的Shell命令的名称。

70
dogbane

要逐行处理命令行的输出( 解释 ):

jobs |
while IFS= read -r line; do
  process "$line"
done

如果数据已经在变量中:

printf %s "$foo" | …

printf %s "$foo"echo "$foo"几乎相同,但是按字面意义打印$foo,而echo "$foo"可能会将$foo解释为echo命令的选项(如果它以-开头),并且可能会在某些shell中以$foo扩展反斜杠序列。

请注意,在某些shell(ash,bash,pdksh,但不是ksh或zsh)中,管道的右侧在单独的进程中运行,因此您在循环中设置的任何变量都会丢失。例如,以下行计数脚本在这些shell中输出0:

n=0
printf %s "$foo" |
while IFS= read -r line; do
  n=$(($n + 1))
done
echo $n

一种解决方法是将脚本的其余部分(或至少需要循环中$n值的部分)放入命令列表中:

n=0
printf %s "$foo" | {
  while IFS= read -r line; do
    n=$(($n + 1))
  done
  echo $n
}

如果对非空行执行的操作足够好并且输入的内容不是很大,则可以使用分词:

IFS='
'
set -f
for line in $(jobs); do
  # process line
done
set +f
unset IFS

说明:将IFS设置为单个换行符将使单词拆分仅在换行符处发生(与默认设置下的任何空白字符相对)。 set -f会关闭通配符(即通配符扩展),否则会在命令替换$(jobs)或变量替代$foo的情况下发生。 for循环作用于$(jobs)的所有片段,它们都是命令输出中的所有非空行。最后,恢复通配符和IFS设置。

28

问题:如果使用while循环,它将在subshel​​l中运行,并且所有变量都将丢失。解决方案:用于循环

# change delimiter (IFS) to new line.
IFS_BAK=$IFS
IFS=$'\n'

for line in $variableWithSeveralLines; do
 echo "$line"

 # return IFS back if you need to split new line by spaces:
 IFS=$IFS_BAK
 IFS_BAK=
 lineConvertedToArraySplittedBySpaces=( $line )
 echo "{lineConvertedToArraySplittedBySpaces[0]}"
 # return IFS back to newline for "for" loop
 IFS_BAK=$IFS
 IFS=$'\n'

done 

# return delimiter to previous value
IFS=$IFS_BAK
IFS_BAK=
16
Dima
jobs="$(jobs)"
while IFS= read
do
    echo $REPLY
done <<< "$jobs"

参考文献:

11
l0b0

在最新的bash版本中,使用mapfilereadarray可以将命令输出有效地读取到数组中

$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305

免责声明:可怕的例子,但是您可以比ls自己想出更好的命令来使用

10
sehe

您可以使用<<<来简单地读取包含换行符分隔的数据的变量:

while read -r line
do 
  echo "A line of input: $line"
done <<<"$lines"
0
Matthew Herrmann