我有一个变量,其中包含命令的多行输出。从变量逐行读取输出的最有效方法是什么?
例如:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
我有一个变量,其中包含命令的多行输出。从变量逐行读取输出的最有效方法是什么?
例如:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
Answers:
您可以使用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命令的名称。
echo
为printf %s
,以便您的脚本即使在非输入输入的情况下也可以使用。
/tmp
目录是可写的,因为它依赖于能够创建临时工作文件。如果您发现自己在/tmp
只读的受限系统上(并且不能被自己更改),则对使用替代解决方案(例如printf
管道)的可能性感到满意。
printf "%s\n" "$var" | while IFS= read -r line
要逐行处理命令行的输出(说明):
jobs |
while IFS= read -r line; do
process "$line"
done
如果数据已经在变量中:
printf %s "$foo" | …
printf %s "$foo"
与几乎相同echo "$foo"
,但按$foo
字面意义打印,而如果以开头,则echo "$foo"
可能会解释$foo
为echo命令的选项-
,并且可能会$foo
在某些shell中扩展反斜杠序列。
请注意,在某些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
关闭glob切换(即通配符扩展),否则会在命令替换$(jobs)
或变量substitutino 的结果下发生$foo
。该for
环作用于的各个部分$(jobs)
,它们都是在命令输出非空行。最后,还原全局和IFS
设置。
local IFS=something
。它不会影响全局范围的值。IIRC,unset IFS
不会让您恢复默认值(如果事先不是默认值,则肯定无法正常工作)。
问题:如果使用while循环,它将在subshell中运行,并且所有变量都将丢失。解决方案:用于循环
# 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=
while read
bash循环到循环中意味着while循环在子shell中,因此变量不是全局的。 while read;do ;done <<< "$var"
使循环体不是子壳。(最近的bash可以选择cmd | while
不像ksh一样将循环的主体放在子shell中。)
在最新的bash版本中,使用mapfile
或readarray
将命令输出有效地读取到数组中
$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305
免责声明:可怕的例子,但是您可以比ls自己想出更好的命令来使用
readarray
一个函数并多次调用该函数。
您可以使用<<<来简单地读取包含换行符分隔的数据的变量:
while read -r line
do
echo "A line of input: $line"
done <<<"$lines"
while IFS= read
...。如果要防止\解释,请使用read -r