使用引号将Bash拆分行分为参数

时间:2016-08-30 17:41:06

标签: linux bash

希望此问题之前没有发生过。至少我找不到答案。也许看起来不太好:(

我们假设我收到了这段文字:

hello "hello" "hello world"

请告诉我为什么这两个脚本有不同的输出?:

1)文本保存在文件

#!/bin/bash
while read line
    do
        set $line
        echo $1
        echo $2
        echo $3
    done < "file"

输出结果为:

hello
"hello"
"hello

2)在脚本中硬编码的文本

#!/bin/bash
set hello "hello" "hello world"
echo $1
echo $2
echo $3

这是输出:

hello
hello
hello world

我想在从不同文件中读取行时获得第二个行为。请堆叠器帮忙:(

2 个答案:

答案 0 :(得分:4)

这是一个shell命令:

set hello "hello" "hello world"

因为它是一个shell命令,所以shell在执行命令之前执行 quote removal 作为最后一步。

将其与文件中的文字进行对比:

$ cat file
hello "hello" "hello world"

当shell读取此文件时,它会将引号视为另一个字符。此文件中的引号永远不会直接出现在命令行中,因此它们不受引用删除的约束。

文档

man bash讨论扩展的部分:

  

报价删除

     

在前面的扩展之后,所有未引用的字符\,'和“的出现都不是由上述之一产生的   扩展被删除。

分词和引用删除如何交互

Bash在引用删除之前会执行分词。例如,这对于此命令很重要:

set hello "hello" "hello world"

当shell执行单词拆分时,它会找到set命令的三个参数。它只是执行set之前的最后一步,shell引用删除。由于没有进行进一步的分词,set的参数数量仍为3。

让我们将上述内容与从文件中读取一行的结果进行对比:

$ cat file
hello "hello" "hello world"
$ read line <file
$ echo "$line"
hello "hello" "hello world"

如上所述,shell不对line的内容删除引号。现在,让我们使用$line作为set的参数:

$ set $line
$ echo $#
4

找到了四个论点。这些论点是:

$ echo 1=$1 2=$2 3=$3 4=$4
1=hello 2="hello" 3="hello 4=world"

如您所见,文件中的引号仅被视为普通普通字符,与he相同。因此,文件中的"hello world"将扩展为两个单词,每个单词都有一个引号字符。

答案 1 :(得分:2)

你可以使用xargs来做你想要的粗略版本,它会解析引号,反斜杠和c。以大致壳等效的方式:

#!/bin/bash
while IFS= read -r line; do
    xargs printf '%s\n' <<<"$line"
done <file

如果您想将这些内容读入位置参数:

#!/bin/bash
while IFS= read -r line; do

    set --                                   # clear the argument list
    while IFS= read -r -d '' element; do     # read a NUL-delimited element
      set -- "$@" "$element"                 # append to the argument list
    done < <(xargs printf '%s\0' <<<"$line") # write NUL-delimited elements

    echo "$1"          # add quotes to make your code less buggy
    echo "$2"
    echo "$3"

done <file