0

In one folder I have two sets of 6 files:

first set:

1st-letter-00 - 1st-letter-05

second set:

verse-00 - verse-05

How can I concatenate these two sets such that the result is one file that has all the files from top to bottom as follows:

Content of 1st-letter-00
Content of verse-00
Content of 1st-letter-01
Content of verse-01
etc.

I hope the question is clear.

1

Two solutions in this answer:

  1. Loop over the files in one set and construct the name of the files in the other, and
  2. Loop over the numbers and construct both filenames in the loop.

One way to pair one set of files with another is to loop over one of the sets and construct the filenames of the other from these. This would be useful in situations where you might not know exactly what the filename suffix may be, or how many files you have in a set, but want to match up any file in one set with files in the other none the less.

for verse in verse-*; do
    letter1=1st-letter-${verse#verse-}

    cat "$letter1" "$verse"
done >result.txt

... or just

for verse in verse-*; do cat "1st-letter-${verse#verse-}" "$verse"; done >result.txt

This loops over all names in the current directory that start with the string verse-. This would include all your verse-00, verse-01 etc. files. If you want to make sure to only match the first six (or if you have other files with the same filename prefix), use the pattern verse-0[0-5] instead of verse-*.

In each iteration, the name of the corresponding 1st-letter file is constructed by removing the verse- prefix from the current value of $verse and prefixing it with the string 1st-letter-. The two files are then concatenated.

The output of the loop as a whole is written to result.txt.

Testing:

$ ls -l
total 48
-rw-r--r--  1 kk  wheel  26 Nov 19 11:48 1st-letter-00
-rw-r--r--  1 kk  wheel  26 Nov 19 11:48 1st-letter-01
-rw-r--r--  1 kk  wheel  26 Nov 19 11:48 1st-letter-02
-rw-r--r--  1 kk  wheel  26 Nov 19 11:48 1st-letter-03
-rw-r--r--  1 kk  wheel  26 Nov 19 11:48 1st-letter-04
-rw-r--r--  1 kk  wheel  26 Nov 19 11:48 1st-letter-05
-rw-r--r--  1 kk  wheel  21 Nov 19 11:48 verse-00
-rw-r--r--  1 kk  wheel  21 Nov 19 11:48 verse-01
-rw-r--r--  1 kk  wheel  21 Nov 19 11:48 verse-02
-rw-r--r--  1 kk  wheel  21 Nov 19 11:48 verse-03
-rw-r--r--  1 kk  wheel  21 Nov 19 11:48 verse-04
-rw-r--r--  1 kk  wheel  21 Nov 19 11:48 verse-05

The loop above is run here.

$ cat result.txt
Contents of 1st-letter-00
Contents of verse-00
Contents of 1st-letter-01
Contents of verse-01
Contents of 1st-letter-02
Contents of verse-02
Contents of 1st-letter-03
Contents of verse-03
Contents of 1st-letter-04
Contents of verse-04
Contents of 1st-letter-05
Contents of verse-05

With a not too old version of bash (the default bash on macOS is too old), or with zsh or yash, you may also loop over the specific zero-filled numbers that you'd like to work with, constructing both filenames in the loop:

for num in {00..05}; do
    cat "1st-letter-$num" "verse-$num"
done >result.txt

With an older version of bash (which does not do zero-filled integers in brace expansions), you could use

for num in 0{0..5}; do
    cat "1st-letter-$num" "verse-$num"
done >result.txt

... and to include numbers over 9, you could use

for num in 0{0..9} {10..99}; do
    cat "1st-letter-$num" "verse-$num"
done >result.txt

This would cover the numbers 00 through to 99.

| improve this answer | |
  • Thank you for your thoughtful reply - because I am just an amature it will take me sometime to study your answer. Again many thanks – wisdomlight Nov 21 '19 at 9:59
  • Thank you - it did what i needed it to do. Can I ask you to explain please, letter1=1st-letter-${verse#verse-} - specifically ${verse#verse-} what does this expression actually do? Many thanks – wisdomlight Nov 21 '19 at 17:04
  • @wisdomlight ${variable#pattern} is a standard variable substitution that removes the shortest prefix from $variable that matches pattern. Changing % to # would remove the shortest suffix. Using %% or ## would remove the longest suffix or prefix. So this means ${verse#verse-} would remove the string verse- from the start of the value in $verse (the loop variable in the code). – Kusalananda Nov 21 '19 at 17:14
0

It very much depends on how flexible your solution needs to be. If you know that these files will always be called 1st-letter-NN and verse-NN, then you can do something like the following in a shell-script

#!/bin/bash

OUTFILE=$1  # take output file from command-line
:>$OUTFILE  # and initialize it as "empty" in case it already existed

for i in {00..05}
do
    cat 1st-letter-$i >> $OUTFILE
    cat verse-$i >> $OUTFILE
done

You could then call this script as in

user@host$ ./my_script.sh merged_content.txt

and the merged content will appear in the file merged_content.txt.

Starting from this, you can extend it to make it more flexible as needed.

| improve this answer | |
  • Rather than initializing the file by truncating (ie :>$OUTFILE), just redirect the script's output to it (eg: exec > $OUTFILE). Then no more redirections are needed. – William Pursell Nov 19 '19 at 11:07
  • @WilliamPursell that is, of course, also a possibility. – AdminBee Nov 19 '19 at 11:12
  • Thanks everybody for the answers and comments. I really appreciate the help the community is offering. As I said to the other person who offered help - I am an amature and so it will take me soem time to study your answers and comments. – wisdomlight Nov 21 '19 at 10:01

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.