テーブルの値でファイルを連結する

テーブルの値でファイルを連結する

私には、それぞれの名前に特定のパターンを含むファイルがいくつかあります。これらのファイルは、ABC1234001データの特定のグループ (複数の列のテーブル) に関する情報を保持しています。また、次のようなテーブルもありますinfo.tsv

group1    ABC1234001    ABC1234010
group2    ABC1234011    ABC1234018
group3    ABC1234019    ABC1234028
...       ...           ...

を含む:

  • 「グループ」列はグループを指定します。
  • 「最初のファイル」列は、対応するグループの情報を含む最初のファイル(アルファベット順)のパターンを指定します。
  • 「最後のファイル」列。対応するグループの情報を含む最後のファイル (アルファベット順) のパターンを指定します。

そこで、各グループのファイルを1つのファイルにまとめる必要があります。

cat ABC123401{1..8}* >> group2.tsv

例として、group2の場合、このinfo.tsvファイルを読み込んでいる間、この例では、すべてのファイル(、、、、、、、、ABC1234011.tsv)がABC1234012.tsv1つのファイルに連結されます。ABC1234013.tsvABC1234014.tsvABC1234015.tsvABC1234016.tsvABC1234017.tsvABC1234018.tsvgroup2.tsv

私がやろうとしていることは次のとおりです。

while read $file; do
  #assign columns to variables like $1="group", $2="firstfile", $3="lastfile"
  cat *{$2..$3}* > $1.tsv;
done < info.tsv

しかし、このアプローチで変数を反復的に変更する方法がよくわかりません。 を使用する方がawk便利なのかもしれませんが、わかりません。 スクリプトは、テーブル内の「最初のファイル」から「最後のファイル」までの対応するファイルの内容を含むgroup1.tsv、、と呼ばれる一連のファイルを生成する必要がgroup2.tsvあります。 そのためのスクリプトの作成を手伝ってください。

答え1

以下のスクリプトでは、連結するすべてのファイルがパターン に一致することを前提としています*.tsv。すべてが に一致することが分かっている場合はABC*.tsv、スクリプトの先頭で の代わりにそのパターンを使用できます*.tsv

*.tsvさらに、スクリプトは、特定のグループに入るすべてのファイル名が、展開されるリストの連続したサブリストとして生成されることを想定しています。

#!/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

スクリプトは、位置パラメータのリストを、 に一致する名前のリストに設定します*.tsv。次に、 から各行の 3 つのフィールドをinfo.tsv変数groupfirst、 に読み込みますlast

この方法で読み取られた各行についてinfo.tsv、位置パラメータのリストがスキャンされ、グループの最初の名前に一致する名前が検索されます。最初の名前が見つかると、フラグ が設定され、collectスクリプトのロジックに、位置パラメータのリストで指定されたファイルから、リスト内の現在の位置からデータの収集を開始するように指示します。これは、グループの最後の名前に対応する名前に遭遇すると終了します。

trueここで、 とは単なる文字列ではなくコマンドとして使用されていることに注意してくださいfalse。変数に格納された値$collectは で実行されるため、スクリプトはまたは のif ! "$collect"2 つのシェル組み込みコマンドのいずれかを実行します。シェルには、他の言語 (Python など) にあるような true または false を表す特別なキーワードはありません。truefalse

テスト:

$ 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

この回答へのコメントで述べたように、私が個人的にこのスクリプトを開発するには、スクリプトを次のようになります。

#!/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

set先頭のコマンドが削除されていること (これはコマンド ライン引数に置き換えられます)、およびリダイレクトが削除されていること (これはコマンド ラインのリダイレクトに置き換えられます) に注意してください。また、コマンド ラインで指定されたパス名のファイル名コンポーネントを保持する変数もinfo.tsv導入しました。filename

次に、次のようにスクリプトを実行します。

$ ./script ABC*.tsv <info.tsv

私がこれによって実現したのは、入力グループ リストがどこに保存されているか、またはその名前が何であるかに依存せず、ABCファイルの名前 (ファイル名のサフィックスがある限り.tsv) や保存場所を気にしないスクリプトです。

答え2

あなたのアプローチは良いアイデアですが、残念ながら、括弧展開内では変数が展開されないため機能しません。

$ echo {1..5}
1 2 3 4 5
$ a=1
$ b=5
$ echo {$a..$b}
{1..5}

ただし、次の方法を使用するとこれを回避できますeval

sed 's/ABC//g' info.tsv | 
    while read -r group start end; do 
        files=( $(eval echo ABC{$start..$end}.tsv) )
        cat "${files[@]}" > "$group.tsv"; 
    done 

ABCこれにより、まずファイルからのすべてのインスタンスが削除されinfo.tsv、数字だけが取得できるようになります。これは、示されたデータ構造とまったく同じであることを前提としていることに注意してください。 がABCグループ名にも存在する場合、これは機能しません。

を削除した後ABC、結果は 、 の 3 つの変数を読み込むループにパイプされます。whileこれら$groupはに渡され、括弧展開を呼び出す前に変数が展開され、ファイル名のリストを取得できるようになります。$start$endeval

$ eval echo ABC{1..5}
ABC1 ABC2 ABC3 ABC4 ABC5

の結果は配列evalに格納され$files、 への入力として渡されますcat

cat "${files[@]}" > "$group.tsv";

答え3

私の理解が正しければ、ここに選択肢があります

$ while IFS= read -r i; do
    f=$(echo "$i" | cut -d' ' -f1)
    cat $(echo "$i" | cut -d' ' -f2- | sed -E 's/([0-9])\s+/\1.tsv /;s/([0-9])$/\1.tsv /') > "$f.txt"
  done < info.tsv

  • f=$(echo "$i" | cut -d' ' -f1)グループの名前を取得します。
  • cat $(cut -d' ' -f2- | sed -E 's/([0-9])\s+|([0-9])$/\1.tsv /g')行内のファイルのリストを連結します。

関連情報