【问题标题】:Linux Scripting with Spaces in Filenames文件名中带有空格的 Linux 脚本
【发布时间】:2017-09-08 16:28:23
【问题描述】:

我目前正在使用供应商提供的软件,该软件试图处理将附件文件发送到另一个脚本,该脚本将从列出的文件中提取文本。当我们从外部源接收包含空格的文件时,脚本会失败,因为供应商提供的软件不会将文件名括在引号中 - 这意味着当运行文本提取脚本时,它会收到一个文件名,该文件名将在空间上分开并导致提取器脚本出错。我们无法编辑供应商提供的软件。

整个过程被设计成自动转移,所以有这个可以随意扔进齿轮的扳手是个问题。

我们正在尝试做的是在我们的文本提取器脚本中处理带空格的名称,因为这是我们可以控制的部分。在快速谷歌之后,似乎更改脚本的 IFS 值将是快速解决方案,但不幸的是,该脚本将在扩展程序已经破坏传入数据后生效。

我正在使用的脚本接受一个 -e 值、一个 -i 值和一个 -o 值。这些值是从供应商提供的脚本发送的,我无法对其进行编辑控制。

#!/bin/bash

usage() { echo "Usage: $0 -i input -o output -e encoding" 1>&2; exit 1; }

while getopts ":o:i:e:" o; do
    case "${o}" in
        i)
            inputfile=${OPTARG}
            ;;
        o)
            outputfile=${OPTARG}
            ;;
        e)
            encoding=${OPTARG}
            ;;
        *)
            usage
            ;;
    esac
done
shift $((OPTIND-1))

...
...
<Uses the inputfile, outputfile, and encoding variables>

我承认,这可能有一些我不完全理解的部分,这可能是一个简单的修复,但我的最终目标是能够提取所有包含 1 的 -o、-i 和 -e值,而不考虑每个部分中的空格。我可以在提取文件名值后处理引用脚本

【问题讨论】:

    标签: linux bash shell scripting whitespace


    【解决方案1】:

    您发布的脚本片段在参数中没有任何空格问题。

    例如,以下内容不需要引用(因为它是一个赋值):

    inputfile=${OPTARG}
    

    脚本中$inputfile 的所有其他用法应该用双引号括起来

    重要的是如何调用这个脚本。

    这会失败并且只会将hello 分配给变量inputfile

    $ ./script.sh -i hello world.txt
    

    字符串world.txt 将提示getopts 函数停止处理命令行,脚本将继续使用shift(之后world.txt 将留在$1 中)。

    以下将正确地将字符串hello world.txt 分配给inputfile

    $ ./script.sh -i "hello world.txt"
    

    应该

    $ ./script.sh -i hello\ world.txt
    

    【讨论】:

    • 我知道在执行脚本之前有办法捕捉到这个,但不幸的是,这个脚本是从另一个我无法修改的程序调用的,所以我必须在它之后处理文件名的拆分输入到这个脚本中。
    • @MeanJerry 你从来没有说过任何关于实际问题的事情。您实际上是否看到了问题?从这个问题看,您似乎只是担心。
    • @MeanJerry,您无法在“输入后”处理拆分。拆分是由在脚本启动之前调用脚本的程序完成的,并且在执行它的 shell 进入内存之前。
    • @MeanJerry, ...查看 execve 系统调用的手册页。这是用于一个程序调用另一个程序的操作系统级接口。你会看到它需要一个参数列表已经被分割成一个单独的 C 字符串每个参数在被执行的程序被调用之前
    • 如何在脚本中使用$inputfile 变量也很重要。你需要到处引用它:"$inputfile" -- 见Security implications of forgetting to quote a variable in bash/POSIX shells
    【解决方案2】:

    以下脚本使用 awk 拆分参数,同时在文件名中包含空格。参数可以是任何顺序。它不处理参数中的多个连续空格,而是将它们折叠为一个。

    #!/bin/bash
    
    IFS=' '
    str=$(printf "%s" "$*")
    
    istr=$(echo "${str}" | awk 'BEGIN {FS="-i"} {print $2}' | awk 'BEGIN {FS="-o"} {print $1}' | awk 'BEGIN {FS="-e"} {print $1}')
    estr=$(echo "${str}" | awk 'BEGIN {FS="-e"} {print $2}' | awk 'BEGIN {FS="-o"} {print $1}' | awk 'BEGIN {FS="-i"} {print $1}')
    ostr=$(echo "${str}" | awk 'BEGIN {FS="-o"} {print $2}' | awk 'BEGIN {FS="-e"} {print $1}' | awk 'BEGIN {FS="-i"} {print $1}')
    
    inputfile=""${istr}""
    outputfile=""${ostr}""
    encoding=""${estr}""
    
    # call the jar
    

    调用 jar 时出现问题,其中 Java 在带有空格的文件名上抛出 MalformedUrlException。

    【讨论】:

      【解决方案3】:

      所以在阅读了评论后,我们决定虽然它可能不是每个场景的正确答案,但这个特定场景的正确答案是手动提取碎片。

      因为我们正在为传递给它的预构建脚本构建它,并且我们不会很快更新该脚本,所以我们可以肯定地接受这个脚本将始终收到 -i、-o 和 - e标志,并且它们之间会有空格,这导致所有传入的片段都存储在$*中的不同变量中。

      我们可以假设标志后面的文本是对标志的响应,直到引用另一个标志。这给我们留下了 3 个场景:

      1. 变量包含其中一个标志
      2. 变量包含紧跟在标志之后的第一部分参数
      3. 变量包含参数的 2+ 部分,名称中的空格被解释为拆分,需要重新插入。

      我一直遇到的其他问题之一是试图让字符串文字等同于我的 IF 语句中的变量。为了解决这个问题,我将所有相关数据预先存储在数组变量中,这样我就可以测试 $variable == $otherVariable。

      虽然我不希望它改变,但我们也处理了如果三个标志出现的顺序与我们预期的不同(我们的假设是它们列为 i,o,e...但我们可以' 不能清楚地看到通过了什么)。参数按读取顺序转储到数组中,并行数组跟踪槽 0,1,2 中的项目是否与 i,o,e 相关。

      最后的结果还是有一个缺陷:如果文件名中连续的空格多于一个,处理前先剪掉空格,我只能占一个空格。但是,当我们在遇到一个带有空格的文件之前处理了超过 4000 个文件时,我发现按照命名约定不太可能遇到带有多个空格的文件。

      到那时,无论如何我们都必须介入进行罕见的干预。

      最终代码改动如下:

      #!/bin/bash
      IFS='|'
      
      position=-1
      ioeArray=("" "" "")
      previous=""
      flagArr=("-i" "-o" "-e" " ")
      ioePattern=(0 1 2)
      
      
      #echo "for loop:"
      for i in $*; do
          #printf "%s\n" "$i"
          if [ "$i" == "${flagArr[0]}" ] || [ "$i" == "${flagArr[1]}" ] || [ "$i" == "${flagArr[2]}" ]; then
              ((position += 1));
              previous=$i;
              case "$i" in
                  "${flagArr[0]}")
                  ioePattern[$position]=0
                  ;;
                  "${flagArr[1]}")
                  ioePattern[$position]=1
                  ;;
                  "${flagArr[2]}")
                          ioePattern[$position]=2
                  ;;
              esac
              continue;
          fi
          if [[ $previous == "-"* ]]; then
              ioeArray[$position]=${ioeArray[$position]}$i;
          else
              ioeArray[$position]=${ioeArray[$position]}" "$i;
          fi
          previous=$i;
      
      done
      
      
      echo "extracting (${ioeArray[${ioePattern[0]}]}) to (${ioeArray[${ioePattern[1]}]}) with (${ioeArray[${ioePattern[2]}]}) encoding."
      
      inputfile=""${ioeArray[${ioePattern[0]}]}"";
      outputfile=""${ioeArray[${ioePattern[1]}]}"";
      encoding=""${ioeArray[${ioePattern[2]}]}"";
      

      【讨论】:

        猜你喜欢
        • 2014-09-26
        • 2012-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多