【问题标题】:Executing commands containing space in Bash在 Bash 中执行包含空格的命令
【发布时间】:2009-05-07 18:41:39
【问题描述】:

我有一个名为 cmd 的文件,其中包含如下 Unix 命令列表:

hostname
pwd
ls  /tmp
cat /etc/hostname
ls -la
ps -ef | grep java
cat cmd

我有另一个脚本在 cmd 中执行命令为:

IFS=$'\n'
clear
for cmds in `cat cmd`
do
        if [  $cmds ] ; then
        $cmds;
        echo "****************************";
        fi
done

问题是 cmd 中不带空格的命令运行良好,但脚本无法正确解释带空格的命令。以下是输出:

patrick-laptop
****************************
/home/patrick/bashFiles
****************************
./prog.sh: line 6: ls  /tmp: No such file or directory
****************************
./prog.sh: line 6: cat /etc/hostname: No such file or directory
****************************
./prog.sh: line 6: ls -la: command not found
****************************
./prog.sh: line 6: ps -ef | grep java: command not found
****************************
./prog.sh: line 6: cat cmd: command not found
****************************

我在这里错过了什么?

【问题讨论】:

    标签: bash shell


    【解决方案1】:

    尝试将一行更改为eval $cmds 而不仅仅是$cmds

    【讨论】:

      【解决方案2】:

      你可以用命令替换你的脚本

      sh cmd
      

      shell 的工作是读取命令并运行它们!如果您需要输出/进度指示器,请以详细模式运行 shell

      sh -v cmd
      

      【讨论】:

        【解决方案3】:

        我个人更喜欢这种方法 - 如果我不必这样做,我不想使用 IFS。如果要在命令中使用管道,则确实需要使用 eval。管道需要由 shell 而不是命令处理。我相信外壳会在扩展字符串之前解析出管道。

        请注意,如果您的 cmd 文件包含需要输入的命令,则会出现问题。 (但你总是可以为读取命令创建一个新的 fd 来读取。)

        clear
        while read cmds
        do
                if [ -n "$cmds" ] ; then
                eval $cmds
                echo "****************************";
                fi
        done < cmd
        

        【讨论】:

        • proDir=${PROJECT_DIR},其中 PROJECT_DIR 包含目录中的空间。尝试 eval ${PROJECT_DIR} 不起作用,有人会给建议吗?
        【解决方案4】:

        编辑: 原来这在管道和重定向上失败了。谢谢,安多玛。

        您需要将IFS 更改回循环内部,以便 bash 知道在哪里拆分参数:

        IFS=$'\n'
        clear
        for cmds in `cat cmd`
        do
            if [ $cmds ] ; then
                IFS=$' \t\n' # the default
                $cmds;
                echo "****************************";
                IFS=$'\n'
            fi
        done
        

        【讨论】:

          【解决方案5】:

          编辑:Ben Blank 的评论指出我的旧答案是错误的,谢谢。

          看起来您正在将命令作为单个字符串执行,因此 bash 将它们视为脚本/可执行文件名称。

          避免这种情况的一种方法是在命令上调用 bash。变化:

              if [  $cmds ] ; then
              $cmds;
              echo "****************************";
              fi
          

              if [  $cmds ] ; then
              bash -c $cmds
              echo "****************************";
              fi
          

          【讨论】:

          • 其实他的问题正好相反。像他在这里所做的那样设置 $IFS 会导致for 完全按照他的意愿在换行符上中断。但是,它会尝试将整个字符串解释为命令名称,而不是带有参数的命令。
          • 通过调用 bash -c 您正在为每个命令启动一个新进程。这似乎太过分了,可能会产生令人惊讶的结果。例如,如果其中一个命令是 cd 命令,则后续命令将不会在该其他目录中运行。
          • 它还可以防止副作用溢出,例如 PATH 中的更改。所以一个单独的过程可能正是你想要的。不过,我确实为您的解决方案投票。
          【解决方案6】:
          sed 'aecho "-----------"' cmd > cmd.sh; bash cmd.sh
          

          sed 'aXX' 将 XX 附加到每一行。这不适用于多行命令,例如:

          for f in * 
          do 
              something $f
          fi
          

          但对于大多数情况下的单行命令,它应该可以。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-14
            • 2015-10-11
            • 2019-04-05
            • 1970-01-01
            • 2014-03-06
            • 2014-05-26
            • 1970-01-01
            • 2013-04-06
            相关资源
            最近更新 更多