7

我有我的字符串,$LINE我想$ITEMS成为这个的数组版本,在单个选项卡上拆分并保留空白。这是我现在的位置:

IFS=$'\n' ITEMS=($(echo "$LINE" | tr "\t" "\n"))

这里的问题IFS是一个或多个,所以它会吞噬换行符、制表符等。我根据此处发布的其他问题尝试了其他一些事情,但他们认为所有字段中总会有一个值,从不空白。似乎掌握关键的那个远远超出了我的范围,并且对整个文件进行了操作(我只是拆分了一个字符串)。

我的偏好是纯 BASH 解决方案。

4

5 回答 5

5

IFS如果字符是空格,则只有一个或多个。非空白字符是单个分隔符。因此,一个简单的解决方案是,如果您确信字符串中没有某些非空白字符,则将制表符转换为该字符,然后对其进行拆分:

IFS=$'\2' read -ra ITEMS <<<"${LINE//$'\t'/$'\2'}"

不幸的是,像“输入中没有实例”这样的假设\2从长远来看往往会失败,其中“从长远来看”转化为“在最坏的可能时间”。因此,您可能希望分两步完成:

IFS=$'\2' read -ra TEMP < <(tr $'\t\2' $'\2\t' <<<"$LINE")
ITEMS=("${TEMP[@]//$'\t'/$'\2'}")
于 2013-11-01T02:48:47.983 回答
2

IFS一种可能性:使用-d选项从字符串中以read制表符结尾的“行”代替拆分。但是,您需要确保您的字符串也以制表符结尾,否则您将丢失最后一项。

items=()
while IFS='' read -r -d$'\t' x; do
   items+=( "$x" )
done <<< $'   foo   \t  bar\nbaz \t   foobar\t'

printf "===%s===\n" "${items[@]}"

确保尾随制表符不添加额外字段可以通过

if [[ $str != *$'\t' ]]; then str+=$'\t'; fi

如有必要。

于 2013-11-01T12:32:26.170 回答
0

IFS 特殊字符:

Words of the form $'string' are treated specially.  The word expands to
string, with backslash-escaped characters replaced as specified by  the
ANSI  C  standard.  Backslash escape sequences, if present, are decoded
as follows:
       \a     alert (bell)
       \b     backspace
       \e
       \E     an escape character
       \f     form feed
       \n     new line
       \r     carriage return
       \t     horizontal tab
       \v     vertical tab
       \\     backslash
       \'     single quote
       \"     double quote
       \?     question mark
       \nnn   the eight-bit character whose value is  the  octal  value
              nnn (one to three digits)
       \xHH   the  eight-bit  character  whose value is the hexadecimal
              value HH (one or two hex digits)
       \uHHHH the Unicode (ISO/IEC 10646) character whose value is  the
              hexadecimal value HHHH (one to four hex digits)
       \UHHHHHHHH
              the  Unicode (ISO/IEC 10646) character whose value is the
              hexadecimal value HHHHHHHH (one to eight hex digits)
       \cx    a control-x character 

扩展的结果是单引号的,就好像美元符号不存在一样。

以美元符号 ($"string") 开头的双引号字符串将导致根据当前语言环境翻译该字符串。如果当前语言环境是 C 或 POSIX,则忽略美元符号。如果字符串被翻译和替换,则替换是双引号。

于 2018-06-30T00:36:28.887 回答
-1
line=$'zero\tone\ttwo'
IFS=$'\t' read -a arr <<< "${line}"
declare -p

输出是

declare -a arr='([0]="zero" [1]="one" [2]="two")'

笔记。这不处理line.

于 2014-10-30T08:23:57.927 回答
-1

一个纯 bash 解决方案,只会在选项卡上拆分,并保留换行符和其他有趣的符号(如果有):

IFS=$'\t' read -r -a arr -d '' < <(printf '%s' "$line")

试试看:

$ line=$'zero\tone with\nnewlines\ttwo\t     three   \n\t\tfive\n'
$ IFS=$'\t' read -r -a arr -d '' < <(printf '%s' "$line")
$ declare -p arr
declare -a arr='([0]="zero" [1]="one with
newlines" [2]="two" [3]="     three   
" [4]="five
")'

如您所见,这完美无缺:它保留了所有内容(空格、换行符等),仅在制表符处拆分。

有一个缺点:它不处理“空字段”:观察 ; 中有两个连续的选项卡line。我们希望在 中得到一个空字段arr,但事实并非如此。

还有一个不太明显的缺点:readis的返回码1,所以从技术上讲,对于 Bash,这个命令是失败的。除非您使用set -eor ,否则这绝对不是问题,set -E但无论如何都不推荐这样做(所以您不应该这样做)。

如果您可以忍受这两个小缺点,那么这可能是理想的解决方案。

请注意,我们使用< <(printf '%s' "$line")而不是<<< "$line"feed read,因为后者插入了一个尾随换行符。

于 2014-10-30T09:25:17.020 回答