
私には、それぞれの名前に特定のパターンを含むファイルがいくつかあります。これらのファイルは、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.tsv
1つのファイルに連結されます。ABC1234013.tsv
ABC1234014.tsv
ABC1234015.tsv
ABC1234016.tsv
ABC1234017.tsv
ABC1234018.tsv
group2.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
変数group
、first
、 に読み込みますlast
。
この方法で読み取られた各行についてinfo.tsv
、位置パラメータのリストがスキャンされ、グループの最初の名前に一致する名前が検索されます。最初の名前が見つかると、フラグ が設定され、collect
スクリプトのロジックに、位置パラメータのリストで指定されたファイルから、リスト内の現在の位置からデータの収集を開始するように指示します。これは、グループの最後の名前に対応する名前に遭遇すると終了します。
true
ここで、 とは単なる文字列ではなくコマンドとして使用されていることに注意してくださいfalse
。変数に格納された値$collect
は で実行されるため、スクリプトはまたは のif ! "$collect"
2 つのシェル組み込みコマンドのいずれかを実行します。シェルには、他の言語 (Python など) にあるような true または false を表す特別なキーワードはありません。true
false
テスト:
$ 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
$end
eval
$ 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')
行内のファイルのリストを連結します。