I need to include a .csv header in multiple .csv files at once by switching the name to:

originalname1c.csv, originalname2c.csv, originalname3c.csv

Currently, the procedure is done as follows:

cat header.csv originalname1.csv > originalname1c.csv
cat header.csv originalname2.csv > originalname2c.csv
cat header.csv originalname3.csv > originalname3c.csv


Is it possible to add header to multiple files as in the above example in a single command?

additional one: what if the filenames were random? keeping the rule from: anyname.csv to anynamec.csv

2 Answers 2

for name in anyname*.csv; do
    cat header.csv "$name" >"${name%.csv}c.csv"

Here, anyname*.csv is some shell pattern, possibly including a directory path that matches the names of the CSV files to which you want to prepend the contents of header.csv. Ideally, this pattern should not match the name header.csv. If it is, consider moving it to some other place, renaming it, or inserting an explicit test to skip it:

for name in anyname*.csv; do
    [ "${name##*/}" = header.csv ] && continue
    cat header.csv "$name" >"${name%.csv}c.csv"

or, in the bash or zsh shell,

for name in anyname*.csv; do
    [ "$name" -ef header.csv ] && continue
    cat header.csv "$name" >"${name%.csv}c.csv"

(Or don't bother with it and delete the headerc.csv file that may be generated.)

The loop iterates over the matched names and creates the new files by concatenating the header with the CSV records, creating a file whose name will be the same as the original file's but with the .csv file name suffix replaced by c.csv.

The standard expansion ${var%pattern} expands to $var, but with the shortest suffix string matching pattern removed. The expansion ${var##pattern} is also standard and removes the longest matching prefix string from $var. One example above uses this to remove any directory path from $name.

for i in 1 2 3; do cat header.csv originalname${i}.csv > originalname${i}c.csv; done


for i in name1 mynameH arglebargle; do cat header.csv ${i}.csv > ${i}c.csv; done

Further, for a bunch of numbered files:

for i in $(seq 1000); do cat header.csv file${i} > file${i}c.csv; done

$(seq 1000) could also be {1..1000}

This gets you

cat header.csv file1 > file1c.csv
cat header.csv file2 > file2c.csv
cat header.csv file3 > file3c.csv
cat header.csv file998 > file998c.csv
cat header.csv file999 > file999c.csv
cat header.csv file1000 > file1000c.csv

A general way to do this is with this line:

for i in *.csv; do if [ "$i" != header.csv ]; then cat header.csv "$i" > $(basename "$i" .csv)c.csv; fi; done

The command "basename" takes apart the path given, and hands back just the filename, and if you have specified the suffix, as we have here with .csv, removes that part as well. Then we add the "c", put the .csv back, and there's the new files name.

The "if" condition skips the header file, otherwise it'll get catted up as well.

  • 1
    for ((i=1; i <= 1000; i++)); do ...; done or for i in {1..1000} avoids the need for seq Oct 25, 2022 at 18:30
  • Cool, I was wondering about that. The Bash manual was less than forthcoming in a quick scan...
    – Hack Saw
    Oct 25, 2022 at 18:33
  • 1
    Yes, the manual is incredibly dense: there are no wasted words in it. The manual online is useful in that you have both a table of contents and an index. You'll want to look at Looping Constructs Oct 25, 2022 at 18:37
  • It didn't work for me. The number of files is random. The file name is random. In that case, how could I do it? cat head.csv random.csv > samerandomnamec.csv
    – sysrq
    Oct 25, 2022 at 20:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .