shell 在从管道读取的while read循环之后重置的变量

5jvtdoz2  于 6个月前  发布在  Shell
关注(0)|答案(4)|浏览(84)
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文件。在这个脚本中,我有几个变量来计算总数(文件,成功的压缩,成功的完整性扫描),但是我似乎遇到了一个问题。
find $loc -name "*.bsp"运行完文件以给予while readwhile read退出时,它会将$filcount$zipcount$scacount(所有这些都在initiate ()bzip ()(在initiate ()期间调用)或bzipint ()(在initiate ()中也调用)内部更改(增加)为零。
为了测试它是否与initiate ()内部的变量变化或从它访问的其他函数有关,我使用了echo $valid,它定义在initiate ()外部(如$filcount$zipcount等),但不会被initiate ()内部或initiate ()本身内部的另一个函数改变。
有趣的是,$valid不会像initiate中的其他变量那样重置为0。
有谁能告诉我为什么我的变量在while read退出时神奇地重置了?

tvz2xvvm

tvz2xvvm1#

如果使用bash

while read
do
    if [ -f "$REPLY.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $REPLY
    fi
    if [ "$scan" == "1" ]; then bzipint $REPLY
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done < <(find $loc -name "*.bsp")

字符串

zvokhttg

zvokhttg2#

我昨天碰到了这个问题。
问题是你正在执行find $loc -name "*.bsp" | while read。因为这涉及到一个管道,所以while read循环实际上不能和脚本的其余部分运行在同一个bash进程中; bash必须派生一个子进程,以便它可以将find的stdout连接到while循环的stdin。
这一切都非常聪明,但这意味着在循环中设置的任何变量在循环后都无法看到,这完全违背了我编写while循环的全部目的。
你可以尝试在不使用管道的情况下向循环提供输入,或者在不使用变量的情况下从循环中获取输出。我最终得到了一个可怕的憎恶,涉及到写入临时文件并将整个循环 Package 在$(...)中,如下所示:

var="$(producer | while read line; do
    ...
    echo "${something}"
done)"

字符串
这让我把var设置为所有从循环中回显的东西。我可能把那个例子的语法搞砸了;我现在手头上没有我写的代码。

tyg4sfes

tyg4sfes3#

在类似POSIX的shell中,使用read在[概念上等同于]管道的末尾总结的选项:
回顾一下:在bash中,默认情况下,以及在严格符合POSIX的shell中,管道中的所有命令都在 * 子shell * 中运行,因此它们创建或修改的变量对 * 当前 * shell
不可见(在管道结束后将不存在)。
下面的内容涵盖了bashkshzshsh([大多数]仅支持POSIX特性的shell,如dash),并展示了避免创建子shell的方法,以保留read创建/修改的变量
如果没有给出最低版本号,假设即使是“相当老”的版本也支持它(有问题的特性已经存在很长时间了,但我不知道它们是什么时候引入的。
请注意,作为下面解决方案的[POSIX兼容]替代方案 *,您可以始终在[临时]文件中捕获命令的输出,然后将其作为 * < file提供给read,这也避免了子shell。

kshzsh:根本不需要解决方法/配置更改:

read内置 * 默认 * 运行在 * 当前 * shell中,用作管道中的 last 命令。
从表面上看,kshzsh * 默认情况下 * 在 * 当前 * shell中管道的 * 最后 * 阶段运行 any 命令。
ksh 93u+zsh 5.0.5中观察到。
如果你知道这个特性是在哪个版本中引入的,请告诉我。

#!/usr/bin/env ksh
#!/usr/bin/env zsh

out= # initialize output variable

# Pipe multiple lines to the `while` loop and collect the values in the output variable.
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

字符串

bash 4.2+:使用lastpipe shell选项

在bash 4.2或更高版本中,打开shell选项lastpipe会导致最后一个管道段在 * 当前 * shell中运行,允许read创建对当前shell可见的变量。

#!/usr/bin/env bash

shopt -s lastpipe # bash 4.2+: make the last pipeline command run in *current* shell

out=
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bashkshzsh:使用process substitution

不严格地说,进程替换是一种让命令的输出像临时文件一样工作的方法。

out=
while read -r var; do
  out+="$var/"
done < <(printf '%s\n' one two three) # <(...) is the process substitution

echo "$out" # -> 'one/two/three'

bashkshzsh:使用here-stringcommand substitution

out=
while read -r var; do
  out+="$var/"
done <<< "$(printf '%s\n' one two three)" # <<< is the here-string operator

echo "$out" # -> 'one/two/three'


请注意,需要对命令替换使用双引号,以保护其输出不受shell expansions影响。

POSIX兼容方案(sh):here-documentcommand substitution一起使用

#!/bin/sh

out=
while read -r var; do
  out="$out$var/"
done <<EOF # <<EOF ... EOF is the here-doc
$(printf '%s\n' one two three)
EOF

echo "$out" # -> 'one/two/three'


请注意,默认情况下,您需要将结束符-在本例中为EOF-放在行的最开头,并且后面不能有任何字符。

esyap4oy

esyap4oy4#

我在这里登陆是因为我经常使用这个范例:

find ${yourPath} -type f -print0 | while IFS= read -rd '' eachFile;do 
    echo "${eachFile}" #show all the files, add more code to do real work
done

字符串
并发现了while循环中的变量在一个子shell中的困难方式,所以这不会产生任何结果:

find ${yourPath} -type f -print0 | while IFS= read -rd '' eachFile;do 
    echo "${eachFile}" #show all the files, add more code to do real work
done
echo "${eachFile}" #we cannot get the last value of eachFile!


这仍然不起作用:

shopt -s lastpipe 
find ${yourPath} -type f -print0 | while IFS= read -rd '' eachFile;do 
    echo "${eachFile}" #show all the files, add more code to do real work
done
echo "${eachFile}" #we cannot get the last value of eachFile; even with lastPipe!


这是可行的:

shopt -s lastpipe 
find ${yourPath} -type f -print0 | while IFS= read -rd '' eachFile;do 
    echo "${eachFile}" #show all the files, add more code to do real work
    latestFile="${eachFile}"
done
echo "${eachFile}" #we cannot get the last value of eachFile; even with lastPipe!
echo "${latestFile}" #but we can get this!


我的猜测是,eachFile变量仍然是在一个子shell中创建的,尽管有lastpipe选项;但是我们可以通过使用在while-loop中定义/设置的变量来规避这个问题。

相关问题