我正在尝试从包含管道(“|”)作为分隔符并包含空格的字符串创建数组。我一直在寻找一段时间,我已经接近了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:
像魅力一样,非常感谢你!我没有尝试过下面的其他建议,但下次我需要做类似的事情。谢谢大家!
答案 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