![逐行迭代多行字符串](https://linux22.com/image/150939/%E9%80%90%E8%A1%8C%E8%BF%AD%E4%BB%A3%E5%A4%9A%E8%A1%8C%E5%AD%97%E7%AC%A6%E4%B8%B2.png)
/bin/sh
我想在 BSD 平台上的POSIX shell ( ) 中处理多行字符串并逐行迭代它。 Bash 不包含在基本 BSD 发行版中,并且具有 GPL 许可证 - 因此我试图使其普遍使用/bin/sh
。
我找到了使用管道的解决方案,但是在常规/bin/sh
外壳中,这些解决方案是在单独的进程中处理的,这意味着以下内容不起作用:
MULTILINE="`cat ${SOMEFILE}`"
SOMEVAR="original value"
echo "${MULTILINE}" | while IFS= read -r SINGLELINE
do
SOMEVAR="updated value"
echo "this is a single line: ${SINGLELINE}"
echo "SOMEVAR is now: ${SOMEVAR}"
done
echo "Final SOMEVAR is unchanged: ${SOMEVAR}"
${SOMEVAR}
在上面的示例中,它实现了我想要的效果,除了对变量的更改(例如在 while 循环之外无法访问)之外。
我的问题:如果没有这个限制,我怎样才能完成类似的事情?请注意,许多解决方案需要 Bash,而我使用的是标准 POSIX-shell /bin/sh
。
答案1
您可以使用此处的文档:
while IFS= read -r SINGLELINE
do
SOMEVAR="updated value"
printf '%s\n' "this is a single line: ${SINGLELINE}"
printf '%s\n' "SOMEVAR is now: ${SOMEVAR}"
done << EOF
$MULTILINE
EOF
printf '%s\n' "Final SOMEVAL is still $SOMEVAR"
根据sh
实现的不同,here-documents 要么实现为已删除的临时文件(其中 shell 预先存储了变量的扩展,后跟换行符),要么实现为管道,shell 将变量的扩展后跟换行符提供给该管道。但无论哪种情况,除了原始的 Bourne shell(现在已不再使用且不是 POSIX 兼容 shell)之外,被重定向的命令都不会在子 shell 中运行(按照 POSIX 的要求)。
或者你可以使用 split+glob:
IFS='
' # split on newline only
set -o noglob
for SINGLELINE in $MULTILINE
do
SOMEVAR="updated value"
printf '%s\n' "this is a single line: ${SINGLELINE}"
printf '%s\n' "SOMEVAR is now: ${SOMEVAR}"
done
printf '%s\n' "Final SOMEVAL is still $SOMEVAR"
但要注意它会跳过空行。
答案2
您可以直接从文件中读取而不需要管道。这可以避免在子 shell 中运行循环,这样您就可以看到循环后while
更改的值。$SOMEVALUE
SOMEVAR="original value"
while IFS= read -r SINGLELINE
do
SOMEVAR="updated value"
printf 'this is a single line: %s\n' "$SINGLELINE"
printf 'SOMEVAR is now: %s\n' "$SOMEVAR"
done <"$SOMEFILE"
printf 'Final SOMEVAR is: %s\n' "$SOMEVAR"
如果您坚持使用$MULTILINE
变量,请将其写入文件并从那里读取:
tmpfile=$(mktemp)
printf '%s\n' "$MULTILINE" >"$tmpfile"
while ...; do
...
done <"$tmpfile"
rm "$tmpfile"
还相关:
上述链接问题的答案还建议以这样的方式编写程序,即所有使用都$SOMEVAR
发生在管道末尾的子 shell 中:
MULTILINE=$(cat "$SOMEFILE")
SOMEVAR="original value"
printf '%s\n' "$MULTILINE" | {
while IFS= read -r SINGLELINE
do
SOMEVAR="updated value"
printf 'this is a single line: %s\n' "$SINGLELINE"
printf 'SOMEVAR is now: %s\n' "$SOMEVAR"
done
printf 'Final SOMEVAR is: %s\n' "$SOMEVAR"
}
也可能相关:
其他可能感兴趣的问题:
答案3
这个对我有用 :
$ cat bin/test
#! /bin/sh
SOMEFILE=$1
MULTILINE="`cat ${SOMEFILE}`"
SOMEVAR="blah"
echo "${MULTILINE}" | while IFS= read -r SINGLELINE
do
echo "this is a single line: ${SINGLELINE}"
echo "but accessing this var fails: ${SOMEVAR}"
done
和
$ bin/test bin/test
this is a single line: #! /bin/sh
but accessing this var fails: blah
this is a single line: SOMEFILE=$1
but accessing this var fails: blah
this is a single line: MULTILINE="`cat ${SOMEFILE}`"
but accessing this var fails: blah
this is a single line: SOMEVAR="blah"
but accessing this var fails: blah
this is a single line:
but accessing this var fails: blah
this is a single line: echo "${MULTILINE}" | while IFS= read -r SINGLELINE
but accessing this var fails: blah
this is a single line: do
but accessing this var fails: blah
this is a single line: echo "this is a single line: ${SINGLELINE}"
but accessing this var fails: blah
this is a single line: echo "but accessing this var fails: ${SOMEVAR}"
but accessing this var fails: blah
this is a single line: done
but accessing this var fails: blah