从管道读取的 while read 循环后重置变量
initiate () {
read -p "Location(s) to look for .bsp files in? " loc
find $loc -name "*.bsp" | while read
do
if [ -f "$loc.bz2" ]
then
continue
else
filcount=$[$filcount+1]
bzip $loc
fi
if [ "$scan" == "1" ]; then bzipint $loc
fi
echo $filcount #Correct counting
echo $zipcount #Correct counting
echo $scacount #Correct counting
echo $valid #Equal to 1
done
echo $filcount #Reset to 0
echo $zipcount #Reset to 0
echo $scacount #Reset to 0
echo $valid #Still equal to 1
}
我正在编写一个 bash shell 脚本,以使用 bzip2 来压缩目录中的所有 .bsp 文件。在此脚本中,我有几个用于计算总数的变量(文件、成功的 zip、成功的完整性扫描),但是我似乎遇到了问题。
当 find $loc -name "*.bsp"
耗尽文件以提供 while read
和 while read
退出时,它会清零 < code>$filcount、$zipcount
和 $scacount
(所有这些都在 initiate ()
内更改(增加),< code>bzip () (即在 initiate ()
期间调用)或 bzipint ()
(也在 initiate ()
中调用),
以测试它是否需要执行 。对于在 initiate ()
内部更改变量或从其访问的其他函数,我使用了 echo $valid
,它是在 initiate ()
外部定义的(喜欢$filcount
、$zipcount
等),但不会从 initiate()
或 initiate() 内的另一个函数更改 有趣的是
,$valid
不会像 initiate 中的其他变量一样重置为 0。
谁能告诉我为什么当 read 退出时我的变量会神奇地重置?
initiate () {
read -p "Location(s) to look for .bsp files in? " loc
find $loc -name "*.bsp" | while read
do
if [ -f "$loc.bz2" ]
then
continue
else
filcount=$[$filcount+1]
bzip $loc
fi
if [ "$scan" == "1" ]; then bzipint $loc
fi
echo $filcount #Correct counting
echo $zipcount #Correct counting
echo $scacount #Correct counting
echo $valid #Equal to 1
done
echo $filcount #Reset to 0
echo $zipcount #Reset to 0
echo $scacount #Reset to 0
echo $valid #Still equal to 1
}
I'm writing a bash shell script to use bzip2
to zip up all .bsp
files inside a directory. In this script I have several variables for counting totals (files, successful zips, successful integrity scans), however I seem to have run into a problem.
When find $loc -name "*.bsp"
runs out of files to give the while read
and while read
exits, it zeros out $filcount
, $zipcount
and $scacount
(all of which are changed (increased) inside initiate ()
, bzip ()
(which is called during initiate ()
) or bzipint ()
(which is also called in initiate ()
).
In order to test if it's something to do with variables changing inside initiate ()
or other functions accessed from it, I used echo $valid
, which is defined outside of initiate ()
(like $filcount
, $zipcount
, etc.), but is not changed from another function inside initiate ()
or inside initiate ()
itself.
Interestingly enough, $valid
does not get reset to 0 like the other variables inside initiate.
Can anyone tell me why my variables magically get reset when while read exits?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果你使用bash
if you use bash
我昨天遇到了这个问题。
问题是你正在做
find $loc -name "*.bsp" |阅读时
。因为这涉及管道,所以while read
循环实际上无法与脚本的其余部分在同一个 bash 进程中运行; bash 必须生成一个子进程,以便它可以将 find 的标准输出连接到 while 循环的标准输入。这一切都非常聪明,但这意味着循环中设置的任何变量在循环之后都看不到,这完全违背了我编写的
while
循环的全部目的。您可以尝试在不使用管道的情况下向循环提供输入,或者在不使用变量的情况下从循环获取输出。我最终得到了一个可怕的令人厌恶的结果,涉及写入临时文件并将整个循环包装在
$(...)
中,如下所示:这让我 var 设置为所有已回显的内容从循环中。我可能搞乱了那个例子的语法;我目前手边没有我写的代码。
I ran into this problem yesterday.
The trouble is that you're doing
find $loc -name "*.bsp" | while read
. Because this involves a pipe, thewhile read
loop can't actually be running in the same bash process as the rest of your script; bash has to spawn off a subprocess so that it can connect the the stdout offind
to the stdin of thewhile
loop.This is all very clever, but it means that any variables set in the loop can't be seen after the loop, which totally defeated the whole purpose of the
while
loop I was writing.You can either try to feed input to the loop without using a pipe, or get output from the loop without using variables. I ended up with a horrifying abomination involving both writing to a temporary file AND wrapping the whole loop in
$(...)
, like so:Which got me var set to all the things that had been echoed from the loop. I probably messed up the syntax of that example; I don't have the code I wrote handy at the moment.
要总结选项,以便在类似 POSIX 的 shell 中在管道[概念上等效]的末尾使用
read
:回顾一下:< code>bash 默认并且在严格符合 POSIX 标准的 shell 中始终,管道中的所有命令都在子 shell 中运行 >,所以变量创建或修改对当前 shell不可见(管道结束后将不存在)。
以下涵盖
bash
、ksh
、zsh
和sh
([大部分]仅限 POSIX 功能的 shell,例如dash
),并展示避免创建子 shell 的方法,以保留通过read
创建/修改的变量强>。如果没有给出最低版本号,则假设即使是“相当旧”的版本也支持它(相关功能已经存在很长时间了,但我不知道具体是什么时候引入的。
请注意,作为以下解决方案的[POSIX 兼容]替代方案,您始终可以在[临时]文件中捕获命令的输出,然后将其提供给
read< /code> as
< file
,这也避免了子 shell。ksh
和zsh
:根本不需要解决方法/配置更改:read
内置默认当用作管道中的last命令时,在当前 shell中运行。看起来,
ksh
和zsh
默认在最后中运行任何命令> 当前 shell 中管道的阶段,如在ksh 93u+
和zsh 5.0.5
中观察到的。如果您具体知道该功能是在哪个版本中引入的,请告诉我。
bash 4.2+
:使用lastpipe
shell 选项在 bash 4.2 或更高版本中,打开 shell 选项
lastpipe
会导致最后一个管道段在当前 shell 中运行,允许读取创建当前 shell 可见的变量。bash
、ksh
、zsh
:使用 进程替换宽泛地说,进程替换是一种使命令的输出充当临时文件的方法。
bash
、ksh
、zsh
:使用 这里字符串 带有 命令替换请注意,需要双引号命令替换以保护其输出 shell 扩展。
符合 POSIX 标准的解决方案 (
sh
):使用 here-document 和命令替换请注意,默认情况下,您需要放置结束分隔符 -
EOF
,在本例中 - 位于行的最开头,并且后面不能有任何字符。To summarize options for using
read
at the end of [the conceptual equivalent of] a pipeline in POSIX-like shells:To recap: in
bash
by default and in strictly POSIX-compliant shells always, all commands in a pipeline run in a subshell, so variables they create or modify won't be visible to the current shell (won't exist after the pipeline ends).The following covers
bash
,ksh
,zsh
, andsh
([mostly] POSIX-features-only shells such asdash
) and shows ways of avoiding the creation of a subshell so as to preserve the variables created / modified byread
.If no minimum version number is given, assume that even "pretty old" versions support it (the features in question have been around for a long time, but I don't know specifically when they were introduced.
Note that, as a [POSIX-compliant] alternative to the solutions below, you can always capture a command's output in a [temporary] file, and then feed it to
read
as< file
, which also avoids subshells.ksh
andzsh
: NO workaround/configuration change needed at all:The
read
builtin by default runs in the current shell when used as the last command in pipeline.Seemingly,
ksh
andzsh
by default run any command in the last stage of a pipeline in the current shell, as observed inksh 93u+
andzsh 5.0.5
.If you know specifically in what version this feature was introduced, let me know.
bash 4.2+
: use thelastpipe
shell optionIn bash version 4.2 or higher, turning on shell option
lastpipe
causes the last pipeline segment to run in the current shell, allowing read to create variables visible to the current shell.bash
,ksh
,zsh
: use process substitutionLoosely speaking, a process substitution is a way to have a command's output act like a temporary file.
bash
,ksh
,zsh
: use a here-string with a command substitutionNote the need to double-quote the command substitution to protect its output from shell expansions.
POSIX-compliant solution (
sh
): use a here-document with a command substitutionNote that, by default, you need to place the ending delimiter -
EOF
, in this case - at the very beginning of the line, and that no characters must follow it.我来到这里是因为我经常使用这种范例:
并发现 while 循环中的变量位于子 shell 中,因此这不会产生任何结果:
这仍然不起作用:
这就是有效的:
Per Mr Duffy: read 的最后一个实例,决定没有更多内容并返回 false,因此循环退出,清除变量。这是故意的行为,因为它可以让您知道流中最后一个 NUL 之后是否有额外的内容:如果在 read 返回 false 后 $eachFile 中有任何内容,则告诉您该内容不是以 NUL 结尾的
I landed here because I frequently use this paradigm:
and found out the hard way that variables within the while loop were in a subshell, so this would yield nothing:
This still doesn't work:
This is what works:
Per Mr Duffy: The last instance of read, the one deciding that there's no more content and returning false so the loop exits, clears the variable. That's deliberate and intentional behavior, because it lets you tell if there's extra content after the last NUL in the stream: if there's anything in $eachFile after read returned false, that tells you that the content wasn't NUL-terminated