可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
How can I join multiple lines into one line, with a separator where the new-line characters were, and avoiding a trailing separator and, optionally, ignoring empty lines?
Example. Consider a text file, foo.txt
, with three lines:
foo
bar
baz
The desired output is:
foo,bar,baz
The command I'm using now:
tr '\n' ',' <foo.txt |sed 's/,$//g'
Ideally it would be something like this:
cat foo.txt |join ,
What's:
- the most portable, concise, readable way.
- the most concise way using non-standard unix tools.
Of course I could write something, or just use an alias. But I'm interested to know the options.
回答1:
Perhaps a little surprisingly, paste
is a good way to do this:
paste -s -d","
This won't deal with the empty lines you mentioned. For that, pipe your text through grep
, first:
grep -v '^$' | paste -s -d"," -
回答2:
This sed
one-line should work -
sed -e :a -e 'N;s/\n/,/;ba' file
Test:
[jaypal:~/Temp] cat file
foo
bar
baz
[jaypal:~/Temp] sed -e :a -e 'N;s/\n/,/;ba' file
foo,bar,baz
To handle empty lines, you can remove the empty lines and pipe it to the above one-liner.
sed -e '/^$/d' file | sed -e :a -e 'N;s/\n/,/;ba'
回答3:
How about to use xargs?
for your case
$ cat foo.txt | sed 's/$/, /' | xargs
Be careful about the limit length of input of xargs command. (This means very long input file cannot be handled by this.)
回答4:
Perl:
cat data.txt | perl -pe 'if(!eof){chomp;$_.=","}'
or yet shorter and faster, surprisingly:
cat data.txt | perl -pe 'if(!eof){s/\n/,/}'
or, if you want:
cat data.txt | perl -pe 's/\n/,/ unless eof'
回答5:
Just for fun, here's an all-builtins solution
IFS=$'\n' read -r -d '' -a data < foo.txt ; ( IFS=, ; echo "${data[*]}" ; )
You can use printf
instead of echo
if the trailing newline is a problem.
This works by setting IFS
, the delimiters that read
will split on, to just newline and not other whitespace, then telling read
to not stop reading until it reaches a nul
, instead of the newline it usually uses, and to add each item read into the array (-a
) data. Then, in a subshell so as not to clobber the IFS
of the interactive shell, we set IFS
to ,
and expand the array with *
, which delimits each item in the array with the first character in IFS
回答6:
I needed to accomplish something similar, printing a comma-separated list of fields from a file, and was happy with piping STDOUT to xargs
and ruby
, like so:
cat data.txt | cut -f 16 -d ' ' | grep -o "\d\+" | xargs ruby -e "puts ARGV.join(', ')"
回答7:
I had a log file where some data was broken into multiple lines. When this occurred, the last character of the first line was the semi-colon (;). I joined these lines by using the following commands:
for LINE in 'cat $FILE | tr -s " " "|"'
do
if [ $(echo $LINE | egrep ";$") ]
then
echo "$LINE\c" | tr -s "|" " " >> $MYFILE
else
echo "$LINE" | tr -s "|" " " >> $MYFILE
fi
done
The result is a file where lines that were split in the log file were one line in my new file.
回答8:
Simple way to join the lines with space in-place using ex
(also ignoring blank lines), use:
ex +%j -cwq foo.txt
If you want to print the results to the standard output, try:
ex +%j +%p -scq! foo.txt
To join lines without spaces, use +%j!
instead of +%j
.
To use different delimiter, it's a bit more tricky:
ex +"g/^$/d" +"%s/\n/_/e" +%p -scq! foo.txt
where g/^$/d
(or v/\S/d
) removes blank lines and s/\n/_/
is substitution which basically works the same as using sed
, but for all lines (%
). When parsing is done, print the buffer (%p
). And finally -cq!
executing vi q!
command, which basically quits without saving (-s
is to silence the output).
Please note that ex
is equivalent to vi -e
.
This method is quite portable as most of the Linux/Unix are shipped with ex
/vi
by default. And it's more compatible than using sed
where in-place parameter (-i
) is not standard extension and utility it-self is more stream oriented, therefore it's not so portable.
回答9:
My answer is:
awk '{printf "%s", ","$0}' foo.txt
printf
is enough. We don't need -F"\n"
to change field separator.