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

9 bash shell while-loop subshell

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
}
Run Code Online (Sandbox Code Playgroud)

我正在编写一个bash shell脚本bzip2来压缩.bsp目录中的所有文件.在这个脚本中,我有几个用于计算总数的变量(文件,成功的拉链,成功的完整性扫描),但是我似乎遇到了问题.

find $loc -name "*.bsp"用完文件的给予while readwhile read退出,其零出$filcount,$zipcount$scacount(所有这些改变(增加)内initiate (),bzip ()(这期间所谓的initiate ())或bzipint ()(也称为initiate ()).

为了测试它是否是与变量内改变initiate ()或从访问等功能,我用的回声$valid,它的外部定义initiate ()(如$filcount,$zipcount等),而不是从里面另一个功能改变initiate ()或内部initiate ()本身.

有趣的是,$valid不会像启动中的其他变量一样重置为0.

任何人都能告诉我为什么我的变量在读取退出时会神奇地重置?

gho*_*g74 10

如果你使用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")
Run Code Online (Sandbox Code Playgroud)

  • +1比传递变量更清晰; 这与管道版本基本相同,但在主进程中使用while循环. (2认同)

mkl*_*nt0 8

为了总结选项用于使用read在[的概念等效]管道的端部在POSIX状壳:

回顾一下:默认情况下在bash中,并且在严格遵守POSIX的shell中,管道中的所有命令都在子shell中运行,因此它们创建或修改的变量对当前 shell 不可见(在管道结束后不存在) ).

下面bash,ksh,zsh,和sh([大多] POSIX的特征只外壳,例如dash),并显示避免子外壳的创建,以便保持创建/修改的变量的方法read.

如果没有给出最低版本号,则假设即使是"相当旧"的版本也支持它(相关功能已经存在了很长时间,但我不知道它们何时被引入.

请注意,作为以下解决方案的[POSIX兼容]替代方案,您始终可以在[临时]文件中捕获命令的输出,然后将其提供read为as < file,这也可以避免子shell.


ksh,和zsh:根本不需要解决方法/配置更改:

当用作管道中的最后一个命令时,read内置默认情况下当前 shell中运行.

看似,ksh并且zsh 在默认情况下运行任何的命令最后在管道的阶段电流外壳.
观察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/'
Run Code Online (Sandbox Code Playgroud)

bash 4.2+:使用lastpipeshell选项

在bash版本4.2或更高版本中,启用shell选项lastpipe会导致最后一个管道段在当前 shell中运行,从而允许读取创建当前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/'
Run Code Online (Sandbox Code Playgroud)

bash,ksh,zsh:使用进程替换

简而言之,进程替换是一种使命令输出像临时文件一样的方法.

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

echo "$out" # -> 'one/two/three'
Run Code Online (Sandbox Code Playgroud)

bash,ksh,zsh:使用下面的字符串使用命令替换

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'
Run Code Online (Sandbox Code Playgroud)

请注意,需要双引号命令替换以保护其输出不受shell扩展的影响.


符合POSIX的解决方案(sh):使用带有命令替换here-document

#!/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'
Run Code Online (Sandbox Code Playgroud)

请注意,默认情况下,您需要将结束分隔符(EOF在本例中)放在行的最开头,并且不必跟随它.


Ben*_*Ben 7

我昨天遇到了这个问题.

麻烦的是你正在做的事情find $loc -name "*.bsp" | while read.因为这涉及管道,所以while read循环实际上不能在与脚本的其余部分相同的bash进程中运行; bash必须生成一个子进程,以便它可以将stdout连接findwhile循环的stdin .

这一切都非常聪明,但这意味着在循环之后无法看到循环中设置的任何变量,这完全打败了while我正在编写的循环的整个目的.

您可以尝试在不使用管道的情况下将输入提供给循环,也可以在不使用变量的情况下从循环中获取输出.我结束了可怕的憎恶,包括写入临时文件和包装整个循环$(...),如下所示:

var="$(producer | while read line; do
    ...
    echo "${something}"
done)"
Run Code Online (Sandbox Code Playgroud)

这让我设置了所有已经从循环中回应的东西.我可能搞砸了那个例子的语法; 我目前没有编写方便的代码.