
異なるディレクトリの複数のファイルをリンクしたいと思います。
ディレクトリ1:Chr1
この例では4つのファイルが含まれています。
ABC.1
DEF.1
GHI.1
JKL.1
カタログ 2:Chr2
ABC.2
DEF.2
GHI.2
JKL.2
22のディレクトリがあります。各ファイルには20個の列とヘッダーがあります。ヘッダーはすべてのファイルに同じです。
すべてを1つのファイル(すべてのディレクトリ内のすべてのファイルをリンクするグローバル出力ファイル)にリンクしたいと思います。
私はこれを試しましたが、うまくいきません。
cat */Chr{1..22}/*.{1..22} > */final_file
ファイルがないため、「該当するファイルやディレクトリはありません」と表示されます。たとえば、* .1〜21はchr22ディレクトリにあるファイルです。
どんな考えがありますか?よろしくお願いします。
答え1
ただzsh
シェルを使用してください:
cat -- */Chr<1-22>/*.<1-22>(n) > final_file
は10進数の範囲に一致するglob演算子であり、glob修飾子はglob拡張が数値でソートされるようにするオプションを切り替えzsh
ます。<x-y>
n
numericglobsort
他のシェルでは、次のことができます。
zsh -c 'cat -- */Chr<1-22>/*.<1-22>(n) > final_file'
最初のファイルを除くすべてのヘッダーをスキップし、GNUまたはBusinessBoxの実装tail
(Linuxをカーネルとして使用するシステムで最も一般的です)を想定するには、次のようにします。
(){
cat < $1; shift; (($#)) && tail -qn +2 -- "$@"
} */Chr<1-22>/*.<1-22>(n) > final_file
答え2
アプローチの問題は、繰り返されるワイルドカードが「同期的に」(=「拡張」)解釈されず、コマンドラインで発生するたびに再解釈され、独立していることです。したがって、機能するには入れ子になったシェルループを使用する必要があります。
次のシェルスクリプトを試してみてください。機能を使用することに注意してくださいbash
(あなたの質問は使用中のシェルをカバーしません)
#!/bin/bash
hdr=0 # initialize variable to keep track of whether the header is already printed
# loop over directories
for d in Chr*
do
# extract trailing number from dir name by removing 'Chr' part (bash feature!)
n="${d#Chr}"
# loop over all files
for f in "$d/"*".$n"
do
if (( hdr == 0 )) # if header wasn't printed yet, output entire file
then
cat "$f" > final_file
hdr=1
else # otherwise, output file content starting with line 2
tail -n +2 "$f" >> final_file
fi
done
done
スクリプト名をconcatenate.sh
実行可能ファイルとして指定し、すべてのサブディレクトリがあるディレクトリで実行できますChr{1..22}
。final_file
このディレクトリにも作成されます。
あまりにも遠くにテストすることはできませんが、何も壊さないでください...
答え3
すべてのサブディレクトリのすべてのファイルをキャプチャするには、Chr.*
次のものを使用できます。
cat Chr*/* >final_file
そのディレクトリ名のサフィックスと一致するように各サブディレクトリのファイルセットを制限する必要がある場合(したがってChr1
一致するファイルのみを考慮*.1
)、ループが必要です。
shopt nullglob # This is bash-specific
for i in {1..22}
do
cat Chr$i/*.$i
done >final_file
このオプションは、shopt nullglob
一致しないワイルドカード文字をリテラルアスタリスクとして残すのではなく、ワイルドカード文字を削除するようにシェルに指示します。
あるいは、リンクされたファイルから最初のヘッダー行を除くすべてを省略したいように見えるので、この拡張ループはそれを処理できます。
first=yes
for i in {1..22}
do
for f in Chr$i/*.$i
do
[[ -n "$first" ]] && head -n1 "$f" && first=
cat "$f"
done
done >final_file
または、ヘッダー行が最初のファイルの最初の行として存在し、その後に見つかるすべての場所から削除できる場合は、次の構造を使用して削除できます。
for i in {1..22}
do
cat Chr$i/*.$i
done |
awk '$0 != header { print } header == "" { header = $0 }' >final_file