各環境で特定のファイルを上書きして複数のtar.gzファイルを生成する方法は?

各環境で特定のファイルを上書きして複数のtar.gzファイルを生成する方法は?

ルートフォルダがあり、Productsそこに複数のサブフォルダがあります。これまで、各サブフォルダには複数のファイルがあります。簡略化のため、サブフォルダ名は でfolder{number}、ファイル名は と考えたがfiles{number}.json一般的に名前が異なります。

通常、ルートフォルダには20個のサブフォルダがあり、各サブフォルダには最大約30個のファイルが含まれています。

(図1)

Products
├── folder1
│   ├── files1.json
│   ├── files2.json
│   └── files3.json
├── folder2
│   ├── files4.json
│   ├── files5.json
│   └── files6.json
└── folder3
    ├── files10.json
    ├── files7.json
    ├── files8.json
    └── files9.json

tar.gz次のコマンドを実行して、これらすべてを1つのファイルに圧縮しました。

tar cvzf ./products.tgz Products

質問:-

以下のように新しいデザインができました。Productsルートフォルダ内の各サブフォルダには、3つの環境フォルダ(devstageおよび)が含まれていますprod

(図2)

Products
├── folder1
│   ├── dev
│   │   └── files1.json
│   ├── files1.json
│   ├── files2.json
│   ├── files3.json
│   ├── prod
│   │   └── files1.json
│   └── stage
│       └── files1.json
├── folder2
│   ├── dev
│   │   └── files5.json
│   ├── files4.json
│   ├── files5.json
│   ├── files6.json
│   ├── prod
│   │   └── files5.json
│   └── stage
│       └── files5.json
└── folder3
    ├── files10.json
    ├── files7.json
    ├── files8.json
    └── files9.json

たとえば、サブフォルダ内には3つの異なるサブフォルダと異なるサブフォルダがfolder1あり、まったく同じです。サブフォルダ内の各サブフォルダには、上書きされたファイルが含まれています。devstageprodfolder2folder3devstageprodfolder{number}

上記の構造では、各tar.gzファイルごとに1つずつ3つの異なるファイルを作成する必要があります。devstageprod

  • その中にどんなファイルがあっても、サブフォルダdev(folder1、folder2、folder3)にもサブフォルダファイルがある場合、stageそのファイルは上書きされます。prod
  • したがって、サブフォルダfiles1.jsonに存在folder1し、同じファイルがどのファイルにも存在するdev場合は、パッケージングstage時にprodその環境フォルダ内のすべてのエントリを使用し、そのサブフォルダファイルを上書きする必要があります。それ以外の場合は、そのサブフォルダ内に存在するすべてのコンテンツフォルダを使用できます。

結局、以下のように3つの異なる構造を持つことになります。 1つはフォルダ1(または2と3)のdev1つが上書きされているため、その環境ファイルの最初の選択肢になります。 、他のファイルは上書きされませんでした。stageprod

(画像3)

Products
├── folder1
│   ├── files1.json
│   ├── files2.json
│   └── files3.json
├── folder2
│   ├── files4.json
│   ├── files5.json
│   └── files6.json
└── folder3
    ├── files10.json
    ├── files7.json
    ├── files8.json
    └── files9.json

products-dev.gz同様ですが、各環境データに固有の合計を生成する必要があります。唯一の違いは、フォルダ1(2または3)の各サブフォルダには、デフォルトの上書きとして特定の環境フォルダのファイルがあり、残りのファイルはそのサブフォルダでのみ使用されることです。products-stage.gzproducts-prod.gzfigure 2figure 3

一部のLinuxコマンドでこれを実行できますか?唯一の混乱は、特定のサブフォルダ内の特定の環境ファイルをオーバーライドしてから、tar.gz3つの異なるファイルを生成する方法です。

修正する:

また、次の点を考慮してください。

Products
├── folder1
│   ├── dev
│   │   ├── files1.json
│   │   └── files5.json
│   ├── files1.json
│   ├── files2.json
│   ├── files3.json
│   ├── prod
│   │   ├── files10.json
│   │   └── files1.json
│   └── stage
│       └── files1.json
├── folder2
│   ├── dev
│   ├── prod
│   └── stage
└── folder3
    ├── dev
    ├── prod
    └── stage

ご覧のとおり、folder2環境folder3のオーバーライドフォルダがありますが、ファイルがないため、この場合はfolder2各環境固有のファイルに空のファイルを作成しようとしています。folder3tar.gz

ベストアンサー1

さまざまなアプローチがありますが、すべてのアプローチには、適用範囲の状況を処理するためにある程度の複雑さが必要です。

少し長いですが、1行に1回の繰り返しでこれを行うことができます。つまり、1つの「環境」ディレクトリです。

(r=Products; e=stage; (find -- "$r" -regextype posix-extended -maxdepth 2 \( -regex '^[^/]+(/[^/]+)?' -o ! -type d \) -print0; find -- "$r" -mindepth 1 -path "$r/*/$e/*" -print0) | tar --null --no-recursion -czf "$r-$e.tgz" -T- --transform=s'%^\(\([^/]\{1,\}/\)\{2\}\)[^/]\{1,\}/%\1%')

よりよく見るために分解すると、次のようになります。

(
    r=Products; e=stage
    (
        find -- "$r" -regextype posix-extended -maxdepth 2 \( -regex '^[^/]+(/[^/]+)?' -o ! -type d \) -print0
        find -- "$r" -mindepth 1 -path "$r/*/$e/*" -print0
    ) \
        | tar --null --no-recursion -czf "$r-$e.tgz" -T- \
            --transform=s'%^\(\([^/]\{1,\}/\)\{2\}\)[^/]\{1,\}/%\1%'
)

指示:

  1. GNU ツールの構文を表示します。 BSDの場合は、justをjustに置き換える必要がありfindます(< - 最後を参照)。-regextype posix-extended-Etar--no-recursion-n--transform=ss-s
  2. デモを簡素化するために、コードスニペットは含まれているディレクトリで実行されると仮定し、保持する「環境」ディレクトリ名のカスタム変数Productsと名前を含む短い名前のヘルパー変数を使用します。$e$rProducts
  3. コマンドラインから実行する$rと、シェルを汚染しないように括弧で囲み、サブシェルにします。$e
  4. 元のファイルをコピーしたりリンク/参照したり、有効なファイル名を処理したり、メモリ制限がなく、すべての名前を処理したりできます。唯一の仮定は、ディレクトリ階層の最初の2つのレベルです。 1レベル以下のディレクトリは「環境」ディレクトリと見なされるため無視されます(に示されているディレクトリを除く$e)。

for e in dev prod stage; do ...; doneシェルループにコードスニペットを含めるだけです。 (最も外側のブラケットを外し、ループfor全体を囲むこともできます)。

良い点は、かなり短く、比較的簡単なことです。

欠点は、常に保持されることです。みんなこれ覆われたfindファイル(デフォルトファイルなど)の場合、デュアルコマンドは最初にtar上書きするファイルを提供するため、抽出プロセス中にそのファイルがオーバーレイファイル(「環境」固有のファイルなど)で上書きされます。これにより、より大きなアーカイブの作成と抽出に時間がかかり、この「オーバーヘッド」が無視できるかどうかによっては望ましくない可能性があります。

エッセイで説明されているパイプラインは次のとおりです。

  1. (最も外側の括弧と補助変数を除く)
  2. 最初のfindコマンドは非特定のファイルのリスト(および更新に応じたブートディレクトリ)のみを生成し、2番目のコマンドはすべてのfind環境関連ファイルのリストのみを生成します。
  3. 両方のコマンド自体は括弧で囲まれ、出力が順番にfindパイプに入ります。tar
  4. tarこれらのパイプを読み取ってファイル名を取得し、そのファイルをアーカイブに保存すると同時に、各--transformファイルのパス名から「環境」コンポーネント(存在する場合)を削除して名前を変更します。
  5. 2つfindのコマンドは1つではなく別々であり、環境固有のファイルよりも非特定のファイルが(使用のために)最初に生成されるように順番に実行されます。tarこれにより、前述のトリックが可能になります。

包含によるオーバーヘッドを避けるためいつもみんな上書きしたファイルを実際に消去するには、追加の複雑さが必要です。 1つのアプローチは次のとおりです。

# still a pipeline, but this time I won't even pretend it to be a one-liner

(
r=Products; e=stage; LC_ALL=C
find -- "$r" -regextype posix-extended \( -path "$r/*/$e/*" -o \( -regex '^([^/]+/){2}[^/]+' ! -type d \) -o -regex '^[^/]+(/[^/]+)?' \) -print0 \
    | sed -zE '\%^(([^/]+/){2})([^/]+/)%s%%0/\3\1%;t;s%^%1//%' \
    | sort -zt/ -k 3 -k 1,1n \
    | sort -zut/ -k 3 \
    | sed -zE 's%^[01]/(([^/]+/)|/)(([^/]+/?){2})%\3\2%' \
    | tar --null --no-recursion -czf "$r-$e.tgz" -T- \
        --transform=s'%^\(\([^/]\{1,\}/\)\{2\}\)[^/]\{1,\}/%\1%'
)

参考にするいくつかの点があります。

  1. GNUおよびBSD構文について以前に述べた内容はすべてここに適用されますfindtar
  2. 以前のソリューションと同様に、ディレクトリ階層の最初の 2 つのレベルの前提には制約はありません。
  3. sedここでは、NULLで区切られたI / O(オプション)を処理するためにGNUを使用していますが、これら2つのコマンドをシェルループ(Bashバージョン3以降が必要)または一意であると確信する他の言語に-z簡単に置き換えることができます。推奨事項は次のとおりです。 NULLで区切られたI / Oを処理できるツールを使用する必要があります(たとえば、GNUでこれを実行できます)。 Bashループを使用した代替方法については、以下を参照してください。sedwhile read ...gawk
  4. find私は暗黙の行動に依存しないので、ここではシングルを使用しています。tar
  5. コマンドのパスを開くコマンドジョブ名のsedリストsort
  6. 特に最初のものは、sed「環境」の名前をパスの先頭に移動し、0その前に補助番号を追加します。これは、非環境ファイルの前に並べ替えることを目的としています。なぜなら、私は後者の前にプレフィックスを追加したからです。 、1目的はソートです
  7. この準備では、「eyes」コマンドの名前リストを正規化し、sortすべての名前が「環境」名を使用せず、すべての名前の先頭に同じ数のスラッシュで区切られたフィールドを持つようにします。これはsortキー定義にとって重要です。
  8. 最初のものは、sort最初にファイル名に基づいて並べ替えて同じ名前を並べてから、前のマークの数値をコマンドして、0「環境」特定のファイル(存在する場合)が表示されることを保証することによって適用されます。非特異的対応物の隣に1sed
  9. ファイル名の2番目のsortマージ(オプション)-uは、最初の重複名のみを残します。これは、以前の並べ替えのために存在する場合は常に「環境」別のファイルです。
  10. 最後に、2番目はsed最初の操作をキャンセルして、アーカイブ用のtarファイル名を再作成します。

このように長いパイプの中央部分を探索することに興味がある場合は、その部分がすべて次のものに関連していることを覚えておいてください。なし- 画面に見えにくい名前を区別します。人間に馴染みのある出力を表示するために、中間出力(つまり少なくともStripped tar)をポライトに渡すことができますtr '\0' '\n'。改行を含むファイル名は、画面に2行にわたって表示されることに注意してください。

もちろん、完全にパラメータ化された関数/スクリプトで作成するか、次のように「環境」ディレクトリの任意の名前を自動的に検出していくつかの改善を行うことができます。

重要:対話型シェルではコメントがうまく受け入れられない可能性があるため、コメントに注意してください。

(
export r=Products LC_ALL=C
cd -- "$r/.." || exit
# make arguments out of all directories lying at the second level of the hierarchy
set -- "$r"/*/*/
# then expand all such paths found, take their basenames only, uniquify them, and pass them along xargs down to a Bash pipeline the same as above
printf %s\\0 "${@#*/*/}" \
    | sort -zu \
    | xargs -0I{} sh -c '
e="${1%/}"
echo --- "$e" ---
find -- "$r" -regextype posix-extended \( -path "$r/*/$e/*" -o \( -regex '\''^([^/]+/){2}[^/]+'\'' ! -type d \) -o -regex '\''^[^/]+(/[^/]+)?'\'' \) -print0 \
    | sed -zE '\''\%^(([^/]+/){2})([^/]+/)%s%%0/\3\1%;t;s%^%1//%'\'' \
    | sort -zt/ -k 3 -k 1,1n \
    | sort -zut/ -k 3 \
    | sed -zE '\''s%^[01]/(([^/]+/)|/)(([^/]+/?){2})%\3\2%'\'' \
    | tar --null --no-recursion -czf "$r-$e.tgz" -T- \
        --transform=s'\''%^\(\([^/]\{1,\}/\)\{2\}\)[^/]\{1,\}/%\1%'\''
' packetizer {}
)

sedBashループを使用して最初のコマンドを置き換える例:

(IFS=/; while read -ra parts -d $'\0'; do
    if [ "${#parts[@]}" -gt 3 ]; then
        env="${parts[2]}"; unset parts[2]
        printf 0/%s/%s\\0 "$env" "${parts[*]}"
    else
        printf 1//%s\\0 "${parts[*]}"
    fi
done)

2番目sedのコマンドの場合:

(IFS=/; while read -ra parts -d $'\0'; do
    printf %s "${parts[*]:2:2}" "/${parts[1]:+${parts[1]}/}" "${parts[*]:4}"
    printf \\0
done)

上記のパイプラインでそのコマンドを直接置き換えるにはsed 、両方sh -cのコードスニペットxargsを括弧で囲む必要がありますbash -c

おすすめ記事