逐行迭代多行字符串

逐行迭代多行字符串

/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

相关内容