首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Bourne函数返回变量始终为空

Bourne函数返回变量始终为空
EN

Stack Overflow用户
提问于 2014-08-21 21:01:50
回答 4查看 484关注 0票数 2

下面的Bourne脚本(给定路径)应该测试路径的每个组件是否存在;然后设置一个变量,仅包含实际存在的那些组件。

代码语言:javascript
复制
#! /bin/sh
set -x             # for debugging

test_path() {
  path=""
  echo $1 | tr ':' '\012' | while read component
  do
    if [ -d "$component" ]
    then
      if [ -z "$path" ]
      then path="$component"
      else path="$path:$component"
      fi
    fi
  done
  echo "$path"    # this prints nothing
}

paths=/usr/share/man:\
/usr/X11R6/man:\
/usr/local/man

MANPATH=`test_path $paths`
echo $MANPATH

当运行时,它总是什么都不打印。使用set -x的跟踪是:

代码语言:javascript
复制
+ paths=/usr/share/man:/usr/X11R6/man:/usr/local/man
++ test_path /usr/share/man:/usr/X11R6/man:/usr/local/man
++ path=
++ echo /usr/share/man:/usr/X11R6/man:/usr/local/man
++ tr : '\012'
++ read component
++ '[' -d /usr/share/man ']'
++ '[' -z '' ']'
++ path=/usr/share/man
++ read component
++ '[' -d /usr/X11R6/man ']'
++ read component
++ '[' -d /usr/local/man ']'
++ '[' -z /usr/share/man ']'
++ path=/usr/share/man:/usr/local/man
++ read component
++ echo ''
+ MANPATH=
+ echo

为什么最后的echo $path是空的?$path循环中的while变量是为每次迭代增量设置的。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-08-21 21:11:45

管道运行子shell中涉及的所有命令,包括整个while ...循环。因此,对该循环中变量的所有更改仅限于子shell,而对父shell脚本是不可见的。

解决这一问题的一种方法是将while ...循环和echo放到一个完全在子shell中执行的列表中,这样修改后的变量$pathecho是可见的。

代码语言:javascript
复制
test_path()
{
  echo "$1" | tr ':' '\n' | {
  while read component
    do
      if [ -d "$component" ]
      then
        if [ -z "$path" ]
        then
          path="$component"
        else
          path="$path:$component"
        fi
      fi
    done
    echo "$path"
  }
}

然而,我建议使用这样的方法:

代码语言:javascript
复制
test_path()
{
    echo "$1" | tr ':' '\n' |
    while read dir
    do
        [ -d "$dir" ] && printf "%s:" "$dir"
    done |
    sed 's/:$/\n/'
}

..。但那是品味的问题。

编辑:正如其他人所说的,你所观察到的行为取决于外壳。POSIX标准将流水线命令描述为在子shell中运行,但这不是必需的:

此外,多命令管道的每个命令都位于子subshell环境中;但是,作为扩展,管道中的任何或所有命令都可以在当前环境中执行。

Bash在子shell中运行它们,但是一些shell在主脚本的上下文中运行最后一个命令,当管道中的前面的命令在子shell中运行时。

票数 2
EN

Stack Overflow用户

发布于 2014-08-21 22:28:32

这应该适用于理解函数的Bourne shell (也可以在Bash和其他shell中工作):

代码语言:javascript
复制
test_path() {
  echo $1 | tr ':' '\012' |
  {
  path=""
  while read component
  do
    if [ -d "$component" ]
    then
      if [ -z "$path" ]
      then path="$component"
      else path="$path:$component"
      fi
    fi
  done
  echo "$path"    # this prints nothing
  }
}

内部一组大括号将命令分组为一个单元,因此path只在子subshell中设置,而是从相同的子subshell中回显。

票数 2
EN

Stack Overflow用户

发布于 2014-08-21 23:18:06

为什么最后的回波$path是空的?

直到最近,Bash将为管道的所有组件提供自己的进程,与运行管道的shell进程分开。单独的进程==分开地址空间,并且没有变量共享。

在ksh93和最近的Bash中(可能需要一个shopt设置),shell将运行调用shell中管道的最后一个组件,因此当循环退出时,循环中更改的任何变量都会被保留下来。

完成所需任务的另一种方法是确保echo $path与循环处于相同的进程中,使用括号:

代码语言:javascript
复制
#! /bin/sh
set -x             # for debugging

test_path() {
  path=""
  echo $1 | tr ':' '\012' | ( while read component
  do
    [ -d "$component" ] || continue

    path="${path:+$path:}$component"
  done
  echo "$path"
  )
}

注意:我简化了内部if。没有else,所以可以用快捷方式替换测试。另外,可以使用S{var:+ ...}参数替换技巧将两个路径赋值组合为一个。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25435851

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档