在 POSIX shell (/bin/sh) 中处理变量范围(while 循环中的管道)?
Handling variable scoping (due pipes in while loop) in POSIX shell (/bin/sh)?
我遇到了这个问题:
- Bash Script Variable Scope Issue
the right side of a pipeline is ran inside of a sub-shell. Any variables set inside of a sub shell will not be set in the parent shell. To fix this, use redirection instead of a pipeline:
所以,这是一个示例脚本:
NUMCOUNT=0
testlines="Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Duis sem sapien, congue sed lacus nec, scelerisque bibendum arcu.
Pellentesque placerat dapibus erat eu iaculis.
Maecenas vitae blandit quam.
Suspendisse eu aliquet nunc."
## this does not work - because NUMCOUNT is not preserved in a subshell - so the final line echoes $NUMCOUNT as 0, which it should not be
#echo "$testlines" | while read line; do
# if echo "$line" | grep -m 1 -q 'que'; then
# NUMCOUNT=$((NUMCOUNT+1))
# echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
# fi
#done
##this works in bash - but for dash (/bin/sh): "Syntax error: redirection unexpected"
#while read line; do
# if echo "$line" | grep -m 1 -q 'que'; then
# NUMCOUNT=$((NUMCOUNT+1))
# echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
# fi
#done <<< "$testlines"
# this works in bash - but for dash (/bin/sh): "Syntax error: redirection unexpected"
while read line; do
if echo "$line" | grep -m 1 -q 'que'; then
NUMCOUNT=$((NUMCOUNT+1))
echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
fi
done < <(echo "$testlines")
echo "Script done - found $NUMCOUNT matches"
如果我在bash
中运行这个,一切正常:
$ bash test.sh
found match nr. 1 for substring 'que' in line: Duis sem sapien, congue sed lacus nec, scelerisque bibendum arcu.
found match nr. 2 for substring 'que' in line: Pellentesque placerat dapibus erat eu iaculis.
found match nr. 3 for substring 'que' in line: Suspendisse eu aliquet nunc.
Script done - found 3 matches
如果我 运行 在 dash
中执行此操作,则它会失败:
$ /bin/sh test.sh
test.sh: 31: test.sh: Syntax error: redirection unexpected
我怎样才能在 POSIX /bin/sh
shell 中正确地实现这个循环,以便在脚本的最后一行保留 $NUMCOUNT?
How can I properly implement this loop in a POSIX /bin/sh shell, so that $NUMCOUNT is preserved for the final line of the script?
在 posix 中,使用带有流式传输数据的后台进程的 fifo。但实际上,在您的情况下,只需将数据保存到文件中...
tmp=$(mktemp)
printf "%s\n" "$testlines" > "$tmp"
while IFS= read -r line; do
if printf "%s\n" "$line" | grep -m 1 -q 'que'; then
...
fi
done < "$tmp"
rm "$tmp"
或者在后台进程中使用 fifo:
f
ifo=$(mktemp -u)
mkfifo "$fifo"
printf "%s\n" "$testlines" > "$fifo" &
while IFS= read -r line; do
if echo "$line" | grep -m 1 -q 'que'; then
NUMCOUNT=$((NUMCOUNT+1))
echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
fi
done < "$fifo"
rm "$fifo"
echo "Script done - found $NUMCOUNT matches"
此外,除了 运行 当前 shell 中的循环,您还可以将其保存在变量中,预处理以提取 NUMCOUNT
并打印其余部分。
tmp=$(
printf "%s\n" "$testlines" |
awk -v q="'" '
/que/{
++NUMCOUNT
print "found match nr. " NUMCOUNT " for substring "q"que"q" in line: "[=12=]
}
END{
# extra last line to output NUMCOUNT
print NUMCOUNT
}'
)
# extract NUMCOUNT - last line
NUMCOUNT=$(printf "%s\n" "$tmp" | tail -n 1)
# extract everything except NUMCOUNT - ie. remove last line
tmp=$(printf "%s\n" "$tmp" | head -n +1)
# output the actual output
printf "%s\n" "$tmp"
# output your trailing line
printf "Script done - found %d matches\n" "$NUMCOUNT"
或者您可以继续在子shell内执行。
printf "%s\n" "$testlines" | {
while IFS= read -r line; do
...
done
echo "Script done - found $NUMCOUNT matches"
# go on
}
备注:bashfaq How can I read a file (data stream, variable) line-by-line (and/or field-by-field) and unix.stackexchange Why is printf better than echo?
我遇到了这个问题:
- Bash Script Variable Scope Issue
the right side of a pipeline is ran inside of a sub-shell. Any variables set inside of a sub shell will not be set in the parent shell. To fix this, use redirection instead of a pipeline:
所以,这是一个示例脚本:
NUMCOUNT=0
testlines="Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Duis sem sapien, congue sed lacus nec, scelerisque bibendum arcu.
Pellentesque placerat dapibus erat eu iaculis.
Maecenas vitae blandit quam.
Suspendisse eu aliquet nunc."
## this does not work - because NUMCOUNT is not preserved in a subshell - so the final line echoes $NUMCOUNT as 0, which it should not be
#echo "$testlines" | while read line; do
# if echo "$line" | grep -m 1 -q 'que'; then
# NUMCOUNT=$((NUMCOUNT+1))
# echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
# fi
#done
##this works in bash - but for dash (/bin/sh): "Syntax error: redirection unexpected"
#while read line; do
# if echo "$line" | grep -m 1 -q 'que'; then
# NUMCOUNT=$((NUMCOUNT+1))
# echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
# fi
#done <<< "$testlines"
# this works in bash - but for dash (/bin/sh): "Syntax error: redirection unexpected"
while read line; do
if echo "$line" | grep -m 1 -q 'que'; then
NUMCOUNT=$((NUMCOUNT+1))
echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
fi
done < <(echo "$testlines")
echo "Script done - found $NUMCOUNT matches"
如果我在bash
中运行这个,一切正常:
$ bash test.sh
found match nr. 1 for substring 'que' in line: Duis sem sapien, congue sed lacus nec, scelerisque bibendum arcu.
found match nr. 2 for substring 'que' in line: Pellentesque placerat dapibus erat eu iaculis.
found match nr. 3 for substring 'que' in line: Suspendisse eu aliquet nunc.
Script done - found 3 matches
如果我 运行 在 dash
中执行此操作,则它会失败:
$ /bin/sh test.sh
test.sh: 31: test.sh: Syntax error: redirection unexpected
我怎样才能在 POSIX /bin/sh
shell 中正确地实现这个循环,以便在脚本的最后一行保留 $NUMCOUNT?
How can I properly implement this loop in a POSIX /bin/sh shell, so that $NUMCOUNT is preserved for the final line of the script?
在 posix 中,使用带有流式传输数据的后台进程的 fifo。但实际上,在您的情况下,只需将数据保存到文件中...
tmp=$(mktemp)
printf "%s\n" "$testlines" > "$tmp"
while IFS= read -r line; do
if printf "%s\n" "$line" | grep -m 1 -q 'que'; then
...
fi
done < "$tmp"
rm "$tmp"
或者在后台进程中使用 fifo: f
ifo=$(mktemp -u)
mkfifo "$fifo"
printf "%s\n" "$testlines" > "$fifo" &
while IFS= read -r line; do
if echo "$line" | grep -m 1 -q 'que'; then
NUMCOUNT=$((NUMCOUNT+1))
echo "found match nr. $NUMCOUNT for substring 'que' in line: $line"
fi
done < "$fifo"
rm "$fifo"
echo "Script done - found $NUMCOUNT matches"
此外,除了 运行 当前 shell 中的循环,您还可以将其保存在变量中,预处理以提取 NUMCOUNT
并打印其余部分。
tmp=$(
printf "%s\n" "$testlines" |
awk -v q="'" '
/que/{
++NUMCOUNT
print "found match nr. " NUMCOUNT " for substring "q"que"q" in line: "[=12=]
}
END{
# extra last line to output NUMCOUNT
print NUMCOUNT
}'
)
# extract NUMCOUNT - last line
NUMCOUNT=$(printf "%s\n" "$tmp" | tail -n 1)
# extract everything except NUMCOUNT - ie. remove last line
tmp=$(printf "%s\n" "$tmp" | head -n +1)
# output the actual output
printf "%s\n" "$tmp"
# output your trailing line
printf "Script done - found %d matches\n" "$NUMCOUNT"
或者您可以继续在子shell内执行。
printf "%s\n" "$testlines" | {
while IFS= read -r line; do
...
done
echo "Script done - found $NUMCOUNT matches"
# go on
}
备注:bashfaq How can I read a file (data stream, variable) line-by-line (and/or field-by-field) and unix.stackexchange Why is printf better than echo?