Linux command to concatenate a file to itself n times

Two parts to this, to me - first - to use cat to output the text file to standard output, and use append to add it to another file - eg foo.txt>>bar.txt will append foo.txt to bar.txt

then run it n times with

for i in {1..n};do cat foo.txt >> bar.txt; done

replacing n in that command with your number

should work, where n is your number

If you use csh, there's the 'repeat' command.

repeat related parts of the answer are copied from here , and i tested it on an ubuntu 11.04 system on the default bash shell.


You certainly can use cat for this:

$ cat /tmp/f
foo
$ cat /tmp/foo /tmp/f
foo
foo

To get $n copies, you could use yes piped into head -n $n:

$ yes /tmp/f | head -n 10
/tmp/f
/tmp/f
/tmp/f
/tmp/f
/tmp/f
/tmp/f
/tmp/f
/tmp/f
/tmp/f
/tmp/f

Putting that together gives

yes /tmp/f | head -n $n | xargs cat >/tmp/output

I am bored so here are a few more methods on how to concatenate a file to itself, mostly with head as a crutch. Pardon me if I overexplain myself, I just like saying things :P


Assuming N is the number of self concatenations you want to do and that your file is named file.

Variables:

linecount=$(<file wc -l)

total_repeats=$(echo "2^$N - 1" | bc) # obtained through the power of MATH

total_lines=$((linecount*(total_repeats+1)))

tmp=$(mktemp --suffix .concat.self)

Given a copy of file called file2, total_repeats is the number of times file would need to be added to file2 to make it the same as if file was concatenated to itself N times.

Said MATH is here, more or less: MATH (gist)

It's first semester computer science stuff but It's been a while since I did a induction proof so I can't get over it... (also this class of recursion is pretty well known to be 2^Loops so there is that too....)


POSIX

I use a few non-posix things but they are not essential. For my purposes:

 yes() { while true; do echo "$1"; done; }

Oh, I only used that. Oh well, the section is already here...


Methods


head with linecount tracking.

ln=$linecount
for i in $(seq 1 $N); do
    <file head -n $ln >> file;
    ln=$((ln*2))
done

No temp file, no cat, not even too much math yet, all joy.


tee with MATH

<file tee -a file | head -n $total_lines > $tmp
cat $tmp > file

Here tee is reading from file but perpetually appending to it, so it will keep reading the file on repeat until head stops it. And we know when to stop it because of MATH. The appending goes overboard through, so I used a temp file. You could trim the excess lines from file too.


eval, the lord of darkness!

eval "cat $(yes file | head -n $((total_repeats+1)) | tr '\n' ' ')" > $tmp
cat $tmp > file

This just expands to cat file file file ... and evals it. You can do it without the $tmp file, too:

eval "cat $(yes file | head -n $total_repeats | tr '\n' ' ')" |
  head -n $((total_lines-linecount)) >> file

The second head "tricks" cat by putting a middle man between it and the write operation. You could trick cat with another cat as well but that has inconsistent behavior. Try this:

test_double_cat() {
    local Expected=0
    local Got=0
    local R=0
    local file="$(mktemp --suffix .double.cat)"
    for i in $(seq 1 100); do

        printf "" > $file
        echo "1" >> $file
        echo "2" >> $file
        echo "3" >> $file

        Expected=$((3*$(<file wc -l)))

        cat $file $file | cat >> $file

        Got=$(<file wc -l)

        [ "$Expected" = "$Got" ] && R="$((R+1))"
    done
    echo "Got it right $R/100"
    rm $file
}

sed:

<file tr '\n' '\0' |
    sed -e "s/.*/$(yes '\0' | head -n $total_repeats | tr -d '\n')/g" |
        tr '\0' '\n' >> file

Forces sed into reading the entire file as a line, captures all of it, then pastes it $total_repeats number of times.

This will fail of course if you have any null characters in your file. Pick one that you know isn't there.

find_missing_char() {
  local file="${1:-/dev/stdin}"

  firstbyte="$(<$file fold -w1 | od -An -tuC | sort -un | head -n 1)"
  if [ ! "$firstbyte" = "0" ]; then
    echo "\0"
  else
    printf "\\$(printf '%03o\t' $((firstbyte-1)) )"
  fi
}

That's all for now lads, I hope this arbitrary answer didn't bother anyone. I tested all of them many times but I am only a two-year shell user so keep that in mind I guess. Now to sleep...

rm $tmp