将字符串bash转换为带有空格和额外分隔符的数组

时间:2011-12-30 19:04:23

标签: arrays string bash delimiter

我正在尝试从包含管道(“|”)作为分隔符并包含空格的字符串创建数组。我一直在寻找一段时间,我已经接近了How do I split a string on a delimiter in Bash?Splitting string into array和更多的来源。我很接近但不太合适。两个主要问题是字符串中有空格,有起始和结束分隔符,有些字段是空白的。此外,我不是只回显值,而是将它们分配给变量。 这是源数据的格式:

|username|full name|phone1|phone2|date added|servers|comments|

示例:

|jdoe | John Doe| 555-1212 | |1/1/11 |  workstation1, server1 | added by me |

这就是我需要的:

Username: jdoe
Fullname: John Doe
Phone1: 555-1212
Phone2: 
Date_added: 1/1/11
Servers: workstation1, server1
Comments: guest account

编辑:我使用sed去除每个分隔符之前和之后的第一个和最后一个分隔符和空格,现在输入:

jdoe|John Doe|555-1212||1/1/11|workstation1, server1|added by me

这是我尝试过的事情:

oIFS="$IFS"; IFS='|'
for line in `cat $userList`; do
  arr=("$line")
  echo "Username: ${arr[0]}"  #not assigning a variable, just testing the output
  echo "Full Name: ${arr[1]}"
  echo "Phone 1: ${arr[2]}"
  echo "Phone 2: ${arr[3]}"
  # etc..
done
IFS="$oIFS"

输出:

Username: 
Full Name: 
Phone 1:
Phone 2:
Username: jdoe
Full Name: 
Phone 1:
Phone 2:
Username: John Doe
Full Name: 
Phone 1:
Phone 2:

我尝试了另一件事:

for line in `cat $userList`; do
  arr=(${line//|/ })
  echo "Username: ${arr[0]}"
  echo "Full Name: ${arr[1]}"
  echo "Phone 1: ${arr[2]}"
  echo "Phone 2: ${arr[3]}"
  # etc
done

输出:

Username: jdoe
Full Name: John
Phone 1:
Phone 2:
Username: Doe
Full Name: 555-1212
Phone 1:
Phone 2:

有什么建议吗?谢谢!


解决:根据下面的第一个建议,脚本现在看起来像这样:

#!/bin/bash
userList=`cat userlist | sed 's/^|//; s/|$//; s/[ ]*|[ ]*/|/g;'`
oIFS="$IFS"; IFS=$'\n'
for line in $userList; do
  IFS='|'
  arr=($line)
  echo "Username: ${arr[0]}"
  echo "Full Name: ${arr[1]}"
  echo "Phone 1: ${arr[2]}"
  echo "Phone 2: ${arr[3]}"
  #etc
done
IFS="$oIFS"

输出:

Username: jdoe
Full Name: John Doe
Phone 1: 555-1212
Phone 2: 
Username: jdoe2
Full Name: Jane Doe
Phone 1: 555-1212
Phone 2:

像魅力一样,非常感谢你!我没有尝试过下面的其他建议,但下次我需要做类似的事情。谢谢大家!

4 个答案:

答案 0 :(得分:9)

你的第一次尝试非常接近。主要问题是:

  • for line in `cat $userList`$IFS拆分文件,而不是按换行符拆分。所以你应该在循环之前设置IFS=$'\n',在循环中设置IFS='|'。 (顺便说一下,值得注意的是for ... in `cat ...`方法读出整个文件然后将其拆分,所以如果文件很大,这不是最好的方法。基于read在这种情况下,方法会更好。)
  • arr=("$line")通过将$line包装在双引号中,可以防止分词,从而使$IFS无关紧要。它应该只是arr=($line)
  • 由于$line有一个前导管道,您需要在到达arr=($line)之前将其剥离(通过编写类似$line="${line#|}"的内容),否则您需要处理{{ 1}}作为一个基于1的数组(因为arr,第一个管道之前的部分将是空的。)

把它放在一起,你得到这样的东西:

${arr[0]}

(注意:我并不担心字段的前导和尾随空格,因为“我可以单独执行该步骤”部分......或者我是否误解了这一点?您是否需要帮助该部分? ?)

答案 1 :(得分:2)

IFS='|'
while read username fullname phone1 phone2 dateadded servers comments; do
    printf 'username: %s\n' "$username"
    printf 'fullname: %s\n' "$fullname"
    printf 'phone1: %s\n' "$phone1"
    printf 'phone2: %s\n' "$phone2"
    printf 'date added: %s\n' "$dateadded"
    printf 'servers: %s\n' "$servers"
    printf 'comments: %s\n' "$comments"
done < infile.txt

答案 2 :(得分:1)

另一种解决方案:

shopt -s extglob

infile='user.lst'
declare -a label=( "" "Username" "Full Name" "Phone 1" "Phone 2"  )

while IFS='|' read  -a fld ; do
  for (( n=1; n<${#label[@]}; n+=1 )); do
    item=${fld[n]}
    item=${item##+([[:space:]])}
    echo  "${label[n]}:  ${item%%+([[:space:]])}"
  done
done < "$infile"

将删除前导和尾随空白。

答案 3 :(得分:0)

使用数组和paste。由于OP表示不是必需的,因此不会考虑空字段。

userList='jdoe|John Doe|555-1212||1/1/11|workstation1, server1|added by me'

fields=("Username: " "Full Name: " "Phone 1: " "Phone 2: " "Date_added: " "Servers: " "Comments: ")

IFS='|' read -ra data <<<${userList}

paste <(IFS=$'\n'; echo "${fields[*]}") <(IFS=$'\n'; echo "${data[*]}")

Username:       jdoe
Full Name:      John Doe
Phone 1:        555-1212
Phone 2: 
Date_added:     1/1/11
Servers:        workstation1, server1
Comments:       added by me