Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have more than 1000 lines in a file. The file starts as follows (line numbers added):

1. Station Name
2. Station Code
3. A N DEV NAGAR
4. ACND
5. ABHAIPUR
6. AHA
7. ABOHAR
8. ABS
9. ABU ROAD
10. ABR

I need to convert this to a file, with comma separated entries by joining every two lines. The final data should look like

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

What I was trying was - trying to write a shell script and then echo them with comma in between. But I guess a simpler effective one-liner would do the job here may be in sed/awk.

Any ideas?

share|improve this question
    
@l0b0 You edited out the OP's remark that the line numbers are "only there for explanation"... –  jasonwryan Oct 18 '12 at 9:15
    
@jasonwryan Sorry, I thought the lines were there for explanation. Parse error at line 0. –  l0b0 Oct 18 '12 at 9:27

7 Answers 7

up vote 10 down vote accepted

Simply use cat (if you like cats ;-)) and paste:

cat file.in | paste -d, - - > file.out

Explanation: paste reads from a number of files and pastes together the corresponding lines (line 1 from first file with line 1 from second file etc):

paste file1 file2 ...

Instead of a file name, we can use - (dash). paste takes first line from file1 (which is stdin). Then, it wants to read the first line from file2 (which is also stdin). However, since the first line of stdin was already read and processed, what now waits on the input stream is the second line of stdin, which paste happily glues to the first one. The -d option sets the delimiter to be a comma rather than a tab.

Alternatively, do

cat file.in | sed "N;s/\n/,/" > file.out

P.S. Yes, one can simplify the above to

< file.in sed "N;s/\n/,/" > file.out

or

< file.in paste -d, - - > file.out

which has the advantage of not using cat.

However, I did not use this idiom on purpose, for clarity reasons -- it is less verbose and I like cat (CATS ARE NICE). So please do not edit.

Alternatively, if you prefer paste to cats (paste is the command to concatenate files horizontally, while cat concatenates them vertically), you may use:

paste file.in | paste -d, - -
share|improve this answer
    
Just to mention it again. Line numbers are not a part of file :) –  mtk Oct 17 '12 at 18:06
    
The paste command perfectly works, can you please give a little more explanation about it. The hyphens ??? –  mtk Oct 17 '12 at 18:08
2  
The hyphens mean "read from stdin". If the same input source is repeated, paste knows to read from it several times per row of output. –  dubiousjim Oct 17 '12 at 18:13
    
@sch: cool edit, I won't touch it :-) –  January Oct 18 '12 at 10:35
    
With respect to your cat argument. Does sed "N;s/\n/,/" file.in > file.out not work? –  Bernhard Oct 18 '12 at 11:13

In case anyone landing here is looking to combine all lines into a CSV one liner, try

cat file | tr '\n' ','
share|improve this answer

For the complete set of answers, a possible awk solution may be:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*
share|improve this answer
    
@downvoter: What is wrong with my answer to deserve a downvote? How can it be improved? –  Bernhard Oct 18 '12 at 20:38
    
Maybe because the lazy printf? Will fail in the rare case when a station name contains a format specifier. (See pastebin.com/wgxFttrJ for an example.) But this is just a guess, the downvote is not from me. –  manatwork Oct 19 '12 at 9:54
paste -sd ',\n' file.in > file.out

Also note that because we're merely replacing one character with another (every other newline with a comma), we can work on the input file in place:

paste -df ',\n' file.in 1<> file.in

(but beware it might not work on non-Unix systems that have CRLF terminators (like Microsoft ones) that some emulated POSIX paste might treat in a non-Unix way)

share|improve this answer

Here is a one-liner (though potentially millions-of-commands-run-er) using pure Bash:

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

I use a subshell (the paranthesis) so that I won't have to store and restore IFS. Which one otherwise should do as to not mess up the users environment in case the source is sourced. The alternative would be to pass that new IFS only to read as in IFS= read -r name, IFS= read -r code.

The fact that all the commands in the loop are built in the shell makes its performance acceptable and is even faster than the other solutions for small files. But many people would consider it bad practice and one should be careful when generalising it to anything else.

share|improve this answer
    
in general yay for using subshells to localize environment changes. But in this case it's not needed: you can instead do while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in, which is an idiom I often see in shell scripts. The -r flag to read means "interpret the character '\' followed by the character 'n' in the stdin stream as two characters, rather than as a newline." Arguably, it may be more aesthetic to create the subshell as you do than to repeat the IFS='\n'. –  dubiousjim Oct 17 '12 at 22:10
    
@dubiousjim: The -r improved the solution technically. Great! I'm not a fan of the idea of passing a changed IFS twice. If I had used one read, super nice, but not twice. Of course that's a matter of opinion. Using a subshell is a bit over the general Bash knowledge I would say, so a lot of folks will have trouble understanding its purpose. That's a bad thing. –  Deleted Oct 17 '12 at 22:41
    
@Gilles: Thanks! –  Deleted Oct 17 '12 at 22:53
sed 'N;s/\n/,/' file

Using sed, join(N) every 2 lines, and replace the newline(\n) with ",".

share|improve this answer

Possible with perl too,

perl -pe 's/^\d+\.\s+//;$.&1?chomp:print","' file

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.