1

I have a file like this one with ID over several lines but I need all records to be on the same line:

8600
22007
93509
9984
22146

to 8600,22007,93509,9984,22146

Even if I can do it in vi with sed 1,$ s/\n/,/g, I need to do it automatically with bash.

I tried sed -i 's/\n/,/g' filename with no luck. Any idea ?

6

with single paste command:

paste -d, -s infile

-s makes the command to print lines in serial with -d, on comma separated.

2

You can use tr

tr '\n' ,

This will change every instance of \n to a comma, so you'll need to fix up the last one. Here's an example

cat >file.txt <<x
8600
22007
93509
9984
22146
x

tr '\n' , <file.txt
8600,22007,93509,9984,22146,        # and the shell prompt follows immediately

tr '\n' , <file.txt | sed 's/,$/\n/'
8600,22007,93509,9984,22146         # line is ended with newline

Note that this use of sed is not defined by POSIX, which expects all lines to be terminated with newline. You can fix this by inserting awk 1 into the pipeline, because awk is explicitly defined by POSIX to add a trailing newline to the final line if one is missing:

tr '\n' , <file.txt | awk 1 | sed 's/,$//'
8600,22007,93509,9984,22146         # line is ended with newline

For a quick fix, this would be acceptable (although the paste solution offered elsewhere is superior). However, tr | awk | sed is an ugly combination that should be revisited in production code. So we can end up with this:

awk 'NR>1 {printf ","}; {printf "%s", $0}; END {printf "\n"}' file.txt
  • Great, I've also tried tr but in the wrong way as I was doing a cat which read the file line by line. – admstg Apr 26 at 9:32
  • 2
    Note that that sed 's/,$/\n/' is not POSIX, first because the behaviour is unspecified if the input doesn't end in a newline character (some seds will ignore that unterminated line), and also because of that \n (POSIXly, you'd use backslash followed by a newline character). Using paste -sd, is much better here. – Stéphane Chazelas Apr 26 at 10:17
1

Using GNU sed and assuming there's no POSIXLY_CORRECT variable in the environment or if there is, that the input has at least two lines:

sed ':a;N;$!ba;s/\n/,/g' input.txt

we can't replace newline. But we can do by appending all line as above.....

0

If you want to do this in pure Bash, you can:

ar=( $(<filename) )
( IFS=, echo "${ar[*]}" > filename )

This makes an array, ar, with the whitespace-separated words of filename as elements, and then prints it out with commas between the items. These are all built-in Bash features.

If you have strange characters (e.g. *) in your data lines, this will misbehave, but for your data it works.

You could also use

tmp=$(printf '%s,' $(<filename))
printf '%s\n' "${tmp%,}" > filename

for the same effect.

  • Since we are assuming Bash: if "misbehave" refers to globbing, couldn't set -f be an effective fix? – fra-san May 9 at 10:19
0

Method1:

perl -pe 's/\n/,/ unless eof' filename

output

8600,22007,93509,9984,22146

Method2(Using Python)

#!/usr/bin/python
m=open('file.txt','r')
j=[]
for i in m:
    j.append(i.strip())
print ",".join(j)

output

python i.py 
8600,22007,93509,9984,22146
0

With awk:

awk '$1=$1' RS= OFS=, infile
0

You can do this transformation multiple ways, some of them being:

$ (head -n -1 - | tr \\n , ;cat -;) < inp

$ perl -lp -0777e 'chop;tr/\n/,/' inp

$ awk '$1=$1' FS="\n" RS= OFS=, inp

$ awk '{
     p = $0
    while ( getline > 0 ) p = p RS $0
     $0 = p
     gsub(/\n/, ",")
 }1' inp

POSIX-sed:

$ sed -e '
    :a
    ${s/\n/,/g;q;}
    N;ba
' inp

GNU-sed:

$ sed -e '
    $!{
        N
        s/^/\n/;D
    }
    y/\n/,/
'  inp


$ perl -0777 -pe 's/\n(?!\z)/,/g' inp

$ perl -lpe '$\ = eof ? $/ : ","' inp
0

Use tr:

$ <input.txt tr "\n" ","
8600,22007,93509,9984,22146,

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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