string bash 字符串到带有空格和额外分隔符的数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8682996/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
bash string to array with spaces and extra delimiters
提问by Martin
I'm trying to create arrays from strings that have pipe ("|") as delimiters and include spaces. I've been looking around for a while and I've gotten close thanks to sources like How do I split a string on a delimiter in Bash?, Splitting string into arrayand a bunch more. I'm close but it's not quite working. The two main problems are that there are spaces in the strings, there are starting and ending delimiters, and some of the fields are blank. Also, instead of just echoing the values, I need to assign them to variables. Here's the format of the source data:
我正在尝试从以管道(“|”)作为分隔符并包含空格的字符串创建数组。我已经环顾了一段时间,并且由于如何在 Bash 中的分隔符上拆分字符串之类的消息来源,我已经接近了。, 将字符串拆分为数组等等。我很接近,但它不太工作。主要的两个问题是字符串中有空格,有起始和结束分隔符,部分字段为空。此外,我需要将它们分配给变量,而不是仅仅回显这些值。这是源数据的格式:
|username|full name|phone1|phone2|date added|servers|comments|
Example:
例子:
|jdoe | John Doe| 555-1212 | |1/1/11 | workstation1, server1 | added by me |
Here's what I need:
这是我需要的:
Username: jdoe
Fullname: John Doe
Phone1: 555-1212
Phone2:
Date_added: 1/1/11
Servers: workstation1, server1
Comments: guest account
Edit: I use sed to strip out the first and last delimiter and spaces before and after each delimiter, input is now:
编辑:我使用 sed 去除每个分隔符前后的第一个和最后一个分隔符和空格,输入现在是:
jdoe|John Doe|555-1212||1/1/11|workstation1, server1|added by me
Here's things I've tried:
这是我尝试过的事情:
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"
Output:
输出:
Username:
Full Name:
Phone 1:
Phone 2:
Username: jdoe
Full Name:
Phone 1:
Phone 2:
Username: John Doe
Full Name:
Phone 1:
Phone 2:
Another thing I tried:
我试过的另一件事:
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
Output:
输出:
Username: jdoe
Full Name: John
Phone 1:
Phone 2:
Username: Doe
Full Name: 555-1212
Phone 1:
Phone 2:
Any suggestions? Thanks!!
有什么建议?谢谢!!
回答by ruakh
Your first attempt is pretty close. The main problems are these:
您的第一次尝试非常接近。主要的问题是这些:
for line in `cat $userList`
splits the file by$IFS
, not by line-breaks. So you should setIFS=$'\n'
before the loop, andIFS='|'
inside the loop. (By the way, it's worth noting that thefor ... in `cat ...`
approach reads out the entire file and then splits it up, so this isn't the best approach if the file can be big. Aread
-based approach would be better in that case.)arr=("$line")
, by wrapping$line
in double-quotes, prevents word-splitting, and therefore renders$IFS
irrelevant. It should just bearr=($line)
.- Since
$line
has a leading pipe, you either need to strip it off before you get toarr=($line)
(by writing something like$line="${line#|}"
), or else you need to treatarr
as a 1-based array (since${arr[0]}
, the part before the first pipe, will be empty).
for line in `cat $userList`
将文件拆分为$IFS
,而不是换行符。所以你应该IFS=$'\n'
在循环之前和循环IFS='|'
内设置。(顺便说一句,值得注意的是,该for ... in `cat ...`
方法读取整个文件,然后将其拆分,因此如果文件很大,这不是最佳方法。read
在这种情况下,基于 - 的方法会更好。)arr=("$line")
,通过$line
用双引号括起来,可以防止分词,因此变得$IFS
无关紧要。它应该只是arr=($line)
。- 由于
$line
有一个前导管道,您要么需要在到达之前将其剥离arr=($line)
(通过编写类似的内容$line="${line#|}"
),要么您需要将其arr
视为基于 1 的数组(因为${arr[0]}
第一个管道之前的部分将为空) .
Putting it together, you get something like this:
把它放在一起,你会得到这样的东西:
oIFS="$IFS"
IFS=$'\n'
for line in `cat $userList`; do
IFS='|'
arr=($line)
echo "Username: ${arr[1]}" #not assigning a variable, just testing the output
echo "Full Name: ${arr[2]}"
echo "Phone 1: ${arr[3]}"
echo "Phone 2: ${arr[4]}"
# etc..
done
IFS="$oIFS"
(Note: I didn't worry about the fields' leading and trailing spaces, because of the "I can do that step separately" part . . . or did I misunderstand that? Do you need help with that part as well?)
(注意:我不担心字段的前导和尾随空格,因为“我可以单独执行那一步”部分......还是我误解了这一点?你也需要这部分的帮助吗?)
回答by kojiro
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
回答by m0dular
Using arrays and paste
. Doesn't account for empty fields since OP said it's not a requirement.
使用数组和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
回答by Fritz G. Mehner
Another solution:
另一种解决方案:
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"
Leading and trailing blanks will be removed.
前导和尾随空格将被删除。