The script below assumes that all files that you may want to concatenate matches the pattern *.tsv
. If you know that they all match ABC*.tsv
, then you may want to use that pattern at the start of the script in place of *.tsv
.
The script furthermore assumes that all names of files that goes into a specific group are generated as a continuous sub-list of the list that *.tsv
expands to.
#!/bin/sh
set -- *.tsv
while read -r group first last; do
collect=false
for name do
if ! "$collect"; then
[ "$name" = "$first.tsv" ] || continue
collect=true
fi
if "$collect"; then
cat -- "$name"
[ "$name" = "$last.tsv" ] && break
fi
done >"$group.tsv"
done <info.tsv
The script sets the list of positional parameters to the list of names matching *.tsv
. It then reads the three fields of each line from info.tsv
into the variables group
, first
and last
.
For each line read from info.tsv
in this way, the list of positional parameters is scanned for names matching the first name in the group. Once this first name is found, we set a flag, collect
, that tells the logic of the script to start collecting the data from the files named in the list of positional parameters, from the current position in the list. This ends once we come across a name that corresponds to the last name of a group.
Note that true
and false
here are being used as commands and not simple strings. The value stored in the variable $collect
is being executed in if ! "$collect"
so that means the script will run one of the two shell builtin commands true
or false
. The shell doesn't have any special keywords for true or false the way some other languages (e.g. Python) do.
Testing:
$ ls
script
$ touch ABC{1234001..1234030}.tsv
$ for name in ABC*.tsv; do printf 'Name: %s\n' "$name" >"$name"; done
$ cat ABC1234015.tsv
Name: ABC1234015.tsv
$ cat >info.tsv <<END_DATA
group1 ABC1234001 ABC1234010
group2 ABC1234025 ABC1234030
END_DATA
$ ./script
$ cat group1.tsv
Name: ABC1234001.tsv
Name: ABC1234002.tsv
Name: ABC1234003.tsv
Name: ABC1234004.tsv
Name: ABC1234005.tsv
Name: ABC1234006.tsv
Name: ABC1234007.tsv
Name: ABC1234008.tsv
Name: ABC1234009.tsv
Name: ABC1234010.tsv
$ cat group2.tsv
Name: ABC1234025.tsv
Name: ABC1234026.tsv
Name: ABC1234027.tsv
Name: ABC1234028.tsv
Name: ABC1234029.tsv
Name: ABC1234030.tsv
As mentioned in comments to this answer, the way I woulddevelop this script for my own personal use would be to leave the script looking like this:
#!/bin/sh
while read -r group first last; do
collect=false
for name do
filename=$( basename "$name" )
if ! "$collect"; then
[ "$filename" = "$first.tsv" ] || continue
collect=true
fi
if "$collect"; then
cat -- "$name"
[ "$filename" = "$last.tsv" ] && break
fi
done >"$group.tsv"
done
Note the deletion of the set
command at the top (this will be replaced by command line arguments), and the deletion of the redirection from info.tsv
(this will be replaced by a redirection on the command line). I have also introduced a filename
variable that will hold the filename component of the pathnames given on the command line.
I would then run the script like so:
$ ./script ABC*.tsv <info.tsv
What I have achieved with this is a script that is agnostic to where the input group list is stored or what it's called, and that does not care what the ABC
files are called (as long as they have a .tsv
filename suffix) or where they are stored.
*{$2..$3}*
can never work in bash! brace expansion happens before variable expansionwhile read group from to; do cat ${from%???????}{${from#???}..${to#???}} > $group.tsv; done