如何用逗号而不是空格分割列表
Posted
技术标签:
【中文标题】如何用逗号而不是空格分割列表【英文标题】:How to split a list by comma not space 【发布时间】:2011-12-04 19:42:54 【问题描述】:我想在for foo in list
中用逗号,
而不是空格 分割文本。假设我有一个 CSV 文件
CSV_File
,其中包含以下文本:
Hello,World,Questions,Answers,bash shell,script
...
我使用以下代码将其拆分为几个单词:
for word in $(cat CSV_File | sed -n 1'p' | tr ',' '\n')
do echo $word
done
打印出来:
Hello
World
Questions
Answers
bash
shell
script
但我希望它用逗号而不是空格分隔文本:
Hello
World
Questions
Answers
bash shell
script
如何在 bash 中实现这一点?
【问题讨论】:
@Carl 你能提供一个使用awk
的例子吗?
【参考方案1】:
使用 subshell 替换来解析单词会撤消您将空格放在一起所做的所有工作。
试试吧:
cat CSV_file | sed -n 1'p' | tr ',' '\n' | while read word; do
echo $word
done
这也增加了并行性。在您的问题中使用子外壳会强制整个子外壳过程完成,然后您才能开始迭代答案。管道到子外壳(如我的回答)让它们并行工作。当然,这仅在文件中有很多行时才重要。
【讨论】:
是的,这是比我建议的要好得多。 +1 l33t bash 技能 mkj :) 甚至不需要while循环。 目前不需要while循环,但我理解echo
的调用作为一些更有趣命令的代理;也就是说,OP 希望 shell 变量中的多字 CSV 内容与其他一些任意命令一起使用。这就是为什么我使用 read 来演示如何将内容放入 shell 变量中。
请注意,如果输入包含换行符,这将无法按预期工作(然后它将在逗号 和 最初出现在输入中的换行符上拆分,即 @987654325 @ 将被拆分为 4 个字段,而不是所需的 3)。对于 Bash,我建议使用单命令范围的 IFS
设置与 read -a
或 read -d
(参见 proper IFS setting in Bash)结合使用,但对于 POSIX shell,我发现 substring processing 是唯一干净且愚蠢的-证明解决方案。【参考方案2】:
将IFS 设置为,:
sorin@sorin:~$ IFS=',' ;for i in `echo "Hello,World,Questions,Answers,bash shell,script"`; do echo $i; done
Hello
World
Questions
Answers
bash shell
script
sorin@sorin:~$
【讨论】:
不错!我完全忘记了 IFS 环境变量! 要在脚本中使用它,您应该将 IFS 变量恢复为之前的值。请参阅 Andrew Newdigate 的回答。 @Sorin:通过“在脚本中使用它”,我的意思是需要更多的代码而不只是这个,因此您希望重置 IFS 以避免任何意外行为。 IFS 的含义似乎相当广泛,所以最好是懒惰而不是不清楚。顺便提一句。如果你像你的答案一样运行你的命令,它会改变当前环境的 IFS,你很容易忘记这一点,然后想知道为什么你的 shell 表现得如此奇怪。【参考方案3】:我认为规范的方法是:
while IFS=, read field1 field2 field3 field4 field5 field6; do
do stuff
done < CSV.file
如果你不知道或不在乎有多少字段:
IFS=,
while read line; do
# split into an array
field=( $line )
for word in "$field[@]"; do echo "$word"; done
# or use the positional parameters
set -- $line
for word in "$@"; do echo "$word"; done
done < CSV.file
【讨论】:
非常方便,可以按名称引用特定字段 @glenn-jackman 你是对的,规范的 UNIX 会使用你的第一种方法。第二个仅适用于 bash 或 zsh 的现代实现。 bash 的read
命令有一个 -a
选项,可以将行中的单词读入数组:while read -a words; do for word in "$words[@]" ...
【参考方案4】:
kent$ echo "Hello,World,Questions,Answers,bash shell,script"|awk -F, 'for (i=1;i<=NF;i++)print $i'
Hello
World
Questions
Answers
bash shell
script
【讨论】:
我假设echo $word
实际上并不是真正需要使用 $word 完成的事情。在这种情况下,您的 awk expession 是在原始问题中执行 sed 和 tr 的另一种方法。我认为 Eng.Fouad 希望在 shell 变量中使用空格的值来做其他事情。
@mkj 此解决方案可以进一步用作 shell 变量,例如:FOO="Hello,World,Questions,Answers,bash shell,script"; BOO=$(echo $FOO | awk -F, 'for (i=1;i<=NF;i++)print $i'); for B in $BOO; do echo "<$B>"; done
@RomanChernyatchik $BOO
上的循环为“bash”和“shell”产生了单独的变量,因此无法按 OP 的预期工作【参考方案5】:
创建一个 bash 函数
split_on_commas()
local IFS=,
local WORD_LIST=($1)
for word in "$WORD_LIST[@]"; do
echo "$word"
done
split_on_commas "this,is a,list" | while read item; do
# Custom logic goes here
echo Item: $item
done
...这会生成以下输出:
Item: this
Item: is a
Item: list
(注意,这个答案已经根据一些反馈进行了更新)
【讨论】:
很奇怪。知道为什么会发生这种情况吗? 副作用在这里解释superuser.com/questions/781766/ifs-separated-items-in-loop 为避免“副作用”,首先将 IFS var 存储在某处OLDIFS=$IFS
,然后执行IFS=, sentences1=($sentences)
,最后恢复 IFS:IFS=$OLDIFS
。否则,这就是我正在寻找的答案。谢谢。
@clime 和 Val,我已更新我的答案以考虑您的反馈。它似乎运作良好,但请告诉我您的想法。
我觉得你的帖子现在太复杂了。修复原始代码 sn-p 并在最后通过一个小注释向评论员致谢就足够了;)。但无论如何,没有什么是完美的。【参考方案6】:
阅读:http://linuxmanpages.com/man1/sh.1.php &http://www.gnu.org/s/hello/manual/autoconf/Special-Shell-Variables.html
IFS 用于分词的内部字段分隔符 展开后,用 read 将行拆分成单词 内置命令。默认值为“”。
IFS 是一个 shell 环境变量,所以它在你的 shell 脚本的上下文中保持不变,除非你导出它。还要注意,IFS 根本不可能从您的环境继承:请参阅此 gnu 帖子了解原因和有关 IFS 的更多信息。
你的代码是这样写的:
IFS=","
for word in $(cat tmptest | sed -n 1'p' | tr ',' '\n'); do echo $word; done;
应该可以,我在命令行上测试过。
sh-3.2#IFS=","
sh-3.2#for word in $(cat tmptest | sed -n 1'p' | tr ',' '\n'); do echo $word; done;
World
Questions
Answers
bash shell
script
【讨论】:
【参考方案7】:你可以使用:
cat f.csv | sed 's/,/ /g' | awk 'print $1 " / " $4'
或
echo "Hello,World,Questions,Answers,bash shell,script" | sed 's/,/ /g' | awk 'print $1 " / " $4'
这是用空格代替逗号的部分
sed 's/,/ /g'
【讨论】:
以上是关于如何用逗号而不是空格分割列表的主要内容,如果未能解决你的问题,请参考以下文章