【问题标题】:echo is different when sed output is assigned to variable from file当 sed 输出分配给文件中的变量时,echo 不同
【发布时间】:2014-05-08 13:01:07
【问题描述】:

我正在编写一个 shell 脚本,它使用 sed 根据模式分割字符串行。

    #pattern 'string1','string2','string3'

    cat $FILENAME | while read LINE
    do

    firstPart=$(echo "$LINE" | sed -r "s/'(.*)','(.*)','(.*)'/\1/" )
    secondPart=$(echo "$LINE" | sed -r "s/'(.*)','(.*)','(.*)'/\2/" )
    thirdPart=$(echo "$LINE" | sed -r "s/'(.*)','(.*)','(.*)'/\3/" )

    done

我可以使用单独的 echo 打印它们,但是如果我将它们放在一个 echo 中,如下所示

    #if LINE from FILE is '123','abc','hello'

    echo "$firstPart $secondPart"
    #this prints " abc" instead of "123 abc"

    #try appending a string on echo
    echo "$firstPart -"
    #this prints " -3" instead of "123 -"

当我尝试在代码中的常量字符串中使用 sed 时,echo 似乎很好。

    #Correct Echo
    SOMESTRING='123','abc','hello'

    firstPart=$(echo "$SOMESTRING" | sed -r "s/'(.*)','(.*)','(.*)'/\1/" )
    secondPart=$(echo "$SOMESTRING" | sed -r "s/'(.*)','(.*)','(.*)'/\2/" )
    thirdPart=$(echo "$SOMESTRING" | sed -r "s/'(.*)','(.*)','(.*)'/\3/" )

    echo "$firstPart $secondPart"
    #this prints "123 abc"

当输入是来自 FILE 的 LINE 时,sed 的行为是否正确?我怎样才能让它表现得好像 LINE 包含在代码中并声明了一样(就像我的第二个示例一样)。

【问题讨论】:

    标签: file bash shell sed echo


    【解决方案1】:

    在我看来,您的字符串中嵌入了回车符(有时写为\r)。因此,当您执行 echo "$firstPart -",其中 firstPart="123\r" 时,它会打印两行:

    123
     -
    

    ...第二个“行”打印在(并覆盖)第一个“行”之上。我的猜测是您已经使用使用 DOS 样式的换行符的文本编辑器编辑了脚本(即每行以回车符后跟换行符结尾),但是 shell 需要 unix 样式的换行符(只是换行符)并处理回车作为命令的一部分(例如 firstPart=$(echo "$SOMESTRING" | sed -r "s/'(.*)','(.*)','(.*)'/\1/" )\r,它将在 firstPart 中包含 \r

    如果这是问题所在,在您的脚本上运行 dos2unix 应该可以解决它(然后切换到不使用 DOS 样式行尾的编辑器)。

    【讨论】:

    • 如果回车在行的中间,dos2unix 将无济于事。你需要tr -d '\r'
    【解决方案2】:

    当你对 bash 说 SOMESTRING='123','abc','hello' 时,它会去掉单引号:

    $ echo "$SOMESTRING"
    123,abc,hello
    

    如果你想保留它们,你需要说SOMESTRING="'123','abc','hello'"

    这意味着您的 sed 模式不起作用,例如firstPart 设置为空字符串。如果您从 sed 模式中删除单引号,它会起作用。

    不过,我建议使用 IFS 来拆分简单的分隔输入:

    echo "'123','abc','hello'" | while IFS=, read a b c; do
      echo $a
      echo $b
      echo $c
    done
    

    或者更简洁:

    while IFS=, read a b c; do echo -e "$a\n$b\n$c"; done <<< "'123','abc','hello'" 
    

    输出:

    '123'
    'abc'
    'hello'
    

    【讨论】:

      【解决方案3】:

      我不是专家,但我可以这么说,当您在管道中使用“while”脚本时,while 中使用的变量在主脚本中不可用。

      例如: 如果在 while 循环中使用 echo 语句,则表明 SED 的解析很好。

      您也可以使用以下语法在主脚本中公开这些变量。

      while read LINE
      do
      
      firstPart=$(echo "$LINE" | sed -r "s/'(.*)','(.*)','(.*)'/\1/" )
      secondPart=$(echo "$LINE" | sed -r "s/'(.*)','(.*)','(.*)'/\2/" )
      thirdPart=$(echo "$LINE" | sed -r "s/'(.*)','(.*)','(.*)'/\3/" )
      done < $FILENAME
      

      【讨论】:

        【解决方案4】:
        SOMESTRING='123','abc','hello'
        
        firstPart=$(echo "$SOMESTRING" | sed "s/\([^,]*\),\([^,]*\),\([^,]*\)/\1/" )
        secondPart=$(echo "$SOMESTRING" | sed "s/\([^,]*\),\([^,]*\),\([^,]*\)/\2/" )
        thirdPart=$(echo "$SOMESTRING" | sed "s/\([^,]*\),\([^,]*\),\([^,]*\)/\3/" )
        

        【讨论】:

        • 请尝试解释您的代码的作用以及它解决问题的原因,谢谢!
        猜你喜欢
        • 2013-06-22
        • 1970-01-01
        • 2014-06-13
        • 2015-08-19
        • 1970-01-01
        • 1970-01-01
        • 2012-12-12
        • 1970-01-01
        相关资源
        最近更新 更多