Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在POSIX shell中迭代双引号字符串?_Shell_For Loop_Posix - Fatal编程技术网

如何在POSIX shell中迭代双引号字符串?

如何在POSIX shell中迭代双引号字符串?,shell,for-loop,posix,Shell,For Loop,Posix,我试图检查脚本所依赖的所有非POSIX命令是否在脚本继续执行其主要任务之前都存在。这将帮助我确保我的脚本不会因为缺少命令而在以后生成错误 我希望将所有此类非POSIX命令的列表保存在名为DEPS的变量中,以便随着脚本的发展和对更多命令的依赖,我可以编辑此变量 我希望脚本支持带有空格的命令,例如my program 这是我的剧本 #!/bin/sh DEPS='ssh scp "my program" sftp' for i in $DEPS do echo "Checking $i .

我试图检查脚本所依赖的所有非POSIX命令是否在脚本继续执行其主要任务之前都存在。这将帮助我确保我的脚本不会因为缺少命令而在以后生成错误

我希望将所有此类非POSIX命令的列表保存在名为
DEPS
的变量中,以便随着脚本的发展和对更多命令的依赖,我可以编辑此变量

我希望脚本支持带有空格的命令,例如
my program

这是我的剧本

#!/bin/sh
DEPS='ssh scp "my program" sftp'

for i in $DEPS
do
    echo "Checking $i ..."
    if ! command -v "$i"
    then
        echo "Error: $i not found"
    else
        echo "Success: $i found"
    fi
    echo
done
但是,这不起作用,因为
“我的程序”
被拆分为两个单词,而
for
循环会迭代:
“我的
程序”
,如下面的输出所示

# sh foo.sh
Checking ssh ...
/usr/bin/ssh
Success: ssh found

Checking scp ...
/usr/bin/scp
Success: scp found

Checking "my ...
Error: "my not found

Checking program" ...
Error: program" not found

Checking sftp ...
/usr/bin/sftp
Success: sftp found
我期望的结果是:

# sh foo.sh
Checking ssh ...
/usr/bin/ssh
Success: ssh found

Checking scp ...
/usr/bin/scp
Success: scp found

Checking my program ...
Error: my program not found

Checking sftp ...
/usr/bin/sftp
Success: sftp found

如何在保持脚本POSIX兼容的同时解决此问题?

之所以会出现这种情况,是因为参数扩展后的步骤是字符串拆分和全局扩展,而不是语法级别的解析(例如处理引用)。要回到解析过程的开始,需要使用
eval


坦率地说,最好的方法是:

  • 以支持阵列(ksh、bash、zsh等)的shell为目标,而不是试图支持POSIX
  • 不要试图从变量中检索值
  • …现代外壳中普遍存在适当的阵列支持是有原因的;编写明确无误的正确代码,特别是在处理不可信数据时,如果没有它就更难了


    也就是说,您可以选择使用
    $@
    来存储内容,可以使用
    eval
    进行设置,尽管这很危险:

    deps='goodbye "cruel world"'
    eval "set -- $deps"
    for program; do
      echo "processing $program"
    done
    
    如果在函数内部执行此操作,将只覆盖函数的参数列表,而不修改全局列表


    或者,
    eval“yourfunction$deps”
    将具有相同的效果,将函数中的参数列表设置为对
    $deps

    的内容运行所有常规解析和扩展阶段的结果这是因为参数扩展后的步骤是字符串拆分和全局扩展,而不是语法级别的解析(例如处理引用)。要回到解析过程的开始,需要使用
    eval


    坦率地说,最好的方法是:

  • 以支持阵列(ksh、bash、zsh等)的shell为目标,而不是试图支持POSIX
  • 不要试图从变量中检索值
  • …现代外壳中普遍存在适当的阵列支持是有原因的;编写明确无误的正确代码,特别是在处理不可信数据时,如果没有它就更难了


    也就是说,您可以选择使用
    $@
    来存储内容,可以使用
    eval
    进行设置,尽管这很危险:

    deps='goodbye "cruel world"'
    eval "set -- $deps"
    for program; do
      echo "processing $program"
    done
    
    如果在函数内部执行此操作,将只覆盖函数的参数列表,而不修改全局列表


    或者,
    eval“yourfunction$deps”
    将具有相同的效果,将函数中的参数列表设置为对
    $deps
    的内容运行所有常规解析和扩展阶段的结果,因为脚本在您的控制下,您可以合理安全地使用
    eval
    ,所以@Charles Duffy的答案是一个简单而好的解决方案。使用它。:)

    也可以考虑使用<代码> AutoCuf生成通常的代码>配置< /COD>脚本,为您所需要的做好工作——例如检查命令和更多…至少,检查一些配置脚本,了解如何解决常见问题

    如果您想使用自己的实现:

    • 将依赖项分为两组
      • core_deps-unix工具,脚本本身通常需要的工具,如
        sed
        cat
        cp
        等。这些程序的名称和$PATH中都不包含空格
      • runtime_deps—程序,应用程序所需的内容,但脚本本身不需要
    • 分两步进行检查(或更多,例如,如果需要检查,例如库)
    • 切勿将
      for
      循环用于以空格分隔的元素,除非将它们作为函数参数-因此可以使用
      “$@”
    作为起始脚本,可以如下所示:

    _check_core_deps() {
        for _cmd
        do
            _cpath=$(command -v "$_cmd")
            case "$_cpath" in
            /*) continue;;
            *) echo "Missing install dependency [$_cmd] - can't continue" ; exit 1 ;;
            esac
        done
        return 0
    }
    
    core_deps="grep sed hooloovoo cp"   #list of "core" commands - they doesn't contains spaces
    _check_core_deps $core_deps || exit 1
    
    以上内容将在不存在的“hooloovoo”命令下爆炸:

    现在您可以安全地继续,安装脚本所需的所有核心命令都可用。在下一步中,您可以检查其他奇怪的依赖项

    一些想法:

    # function what returns your dependecies as lines from HEREDOC
    # (e.g. could contain any character except "\n")
    # you can decorate the dependecies with comments...
    # because we have sed (checked in the 1st step, can use it)
    # if want, you can add "fields" too, for some extended functinality with an specified delimiter
    list_deps() {
        _sptab=$(printf " \t")  # the $' \t' is approved by POSIX for the next version only
        #the "sed" removes comments and empty lines
        #the UUOC (useless use of cat) is intentional here
        #for example if you want add "tr" before the "sed"
        #of course, you can remove it...
        cat - <<DEPS |sed "s/[$_sptab]*#.*//;/^[$_sptab]*$/d"
    ########## DEPENDECIES ############
    #some comment
    ssh
    scp
    sftp
            #comment
    #bla bla
    my program  #some comment
    /Applications/Some Long And Spaced OSX Apllication.app
    DEPS
    ########## END of DEPENDECIES #####
    }
    
    _check_deps() {
    #in the "while" loop you can use IFS=: or such and adding anouter variable to read 
    #for getting more fields for some extended functionality
    list_deps | while read -r line
    do
        #do any checks with the line
        #implement additional functionalities as functions
        #etc...
        #remember - your in an subshell here
        printf "command:%s\n" "$line"
    done
    } 
    
    _check_deps
    
    • 永远不要使用大写的变量,如“DEPS”。。。它们只适用于环境变量

    因为脚本在您的控制下,您可以合理安全地使用
    eval
    ,因此@Charles Duffy的答案是一个简单而好的解决方案。使用它。:)

    也可以考虑使用<代码> AutoCuf生成通常的代码>配置< /COD>脚本,为您所需要的做好工作——例如检查命令和更多…至少,检查一些配置脚本,了解如何解决常见问题

    如果您想使用自己的实现:

    • 将依赖项分为两组
      • core_deps-unix工具,脚本本身通常需要的工具,如
        sed
        cat
        cp
        等。这些程序的名称和$PATH中都不包含空格
      • runtime_deps—程序,应用程序所需的内容,但脚本本身不需要
    • 分两步进行检查(或更多,例如,如果需要检查,例如库)
    • 切勿将
      for
      循环用于空格分隔的元素,除非您将它们作为函数参数,这样您就可以使用
      deps="ssh
      scp
      my program
      sftp
      "
      while read -r cmd; do
          printf "Checking $cmd ...\n"
          if ! command -v "$cmd"; then
              printf "Error: $i not found\n"
          else
              printf "Success: $cmd found\n"
          fi
          printf "\n"
      done <<EOF
      $deps
      EOF