15

I have a text file that holds a list of files. I want to cat their contents together. What is the best way to do this? I was doing something like this but it seems overly complex:

let count=0
while read -r LINE
do
    [[ "$line" =~ ^#.*$ ]] && continue
    if [ $count -ne 0 ] ; then
        file="$LINE"
        while read PLINE
        do
            echo $PLINE | cat - myfilejs > /tmp/out && mv /tmp/out myfile.js
        done < $file
    fi
    let count++
done < tmp

I was skipping commented lines and running into issues. There has to be a better way to do this, without two loops. Thanks!

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
user1470511
  • 339
  • 1
  • 3
  • 14

6 Answers6

19

Or in a simple command

cat $(grep -v '^#' files) > output
Bernhard
  • 3,619
  • 1
  • 25
  • 50
  • 2
    @CharlesDuffy I doubt that for this particular application one would really use space-separated filenames. Rather propose an improvement, as in my opinion a oneliner is a nicer solution than a 8-line script. Are you actually using filenames with whitespace EVER? – Bernhard Jul 25 '12 at 12:42
  • 5
    @Bernhard Don't take this the wrong way, but *of course* we deal with filenames with whitespace all the time. We support users who don't know the difference, and why should they if it's easy enough to write a script that can handle it? – kojiro Jul 25 '12 at 12:47
  • 3
    And if didn't have comments: `cat $(cat files) > output` – mustafa.0x Dec 31 '13 at 10:21
7

xargs cat < files

The advantage of xargs over $(cat) is that cat expands to a huge list of arguments which could fail if you have a lot of files in the list due to Linux' maximum command line length.

Example without caring about #:

printf 'a\nb\nc\n' > files
printf '12\n3\n' > a
printf '4\n56\n' > b
printf  '8\n9\n' > c
xargs cat < files

Output:

12
3
4
56
8
9

More specific example ignoring # as requested by OP:

printf 'a\nb\n#c\n' > files
printf '12\n3\n' > a
printf '4\n56\n' > b
printf  '8\n9\n' > c
grep -v '^#' files | xargs cat

Output:

12
3
4
56

Related: How to pipe list of files returned by find command to cat to view all the files

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
5
#!/bin/bash

files=()
while read; do
    case "$REPLY" in
        \#*|'') continue;;
        *) files+=( "$REPLY" );;
    esac
done < input
cat "${files[@]}"

What's better about this approach is that:

  1. The only external command, cat, only gets executed once.
  2. It's pretty careful to maintain significant whitespace for any given line/filename.
kojiro
  • 74,557
  • 19
  • 143
  • 201
3
{
  while read file
  do
    #process comments here with continue
    cat "$file"
  done
} < tmp > newfile
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

How about cat $(cat listoffiles) | grep -v "^#"?

Lars Kotthoff
  • 107,425
  • 16
  • 204
  • 204
0

I ended up with this:

sed '/^$/d;/^#/d;s/^/cat "/;s/$/";/' files | sh > output

Steps:

  1. sed removes blank lines & commented out lines from files.

  2. sed then wraps each line in cat " and ";.

  3. Script is then piped to sh with and into output.

David Makogon
  • 69,407
  • 21
  • 141
  • 189
sh3rmy
  • 1
  • I forgot to mention that this crudely handles whitespaces by wrapping the file names in quotes. – sh3rmy Mar 20 '17 at 22:18