ルートフォルダがあり、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つの環境フォルダ(dev
、stage
および)が含まれています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
あり、まったく同じです。サブフォルダ内の各サブフォルダには、上書きされたファイルが含まれています。dev
stage
prod
folder2
folder3
dev
stage
prod
folder{number}
上記の構造では、各tar.gz
ファイルごとに1つずつ3つの異なるファイルを作成する必要があります。dev
stage
prod
- その中にどんなファイルがあっても、サブフォルダ
dev
(folder1、folder2、folder3)にもサブフォルダファイルがある場合、stage
そのファイルは上書きされます。prod
- したがって、サブフォルダ
files1.json
に存在folder1
し、同じファイルがどのファイルにも存在するdev
場合は、パッケージングstage
時にprod
その環境フォルダ内のすべてのエントリを使用し、そのサブフォルダファイルを上書きする必要があります。それ以外の場合は、そのサブフォルダ内に存在するすべてのコンテンツフォルダを使用できます。
結局、以下のように3つの異なる構造を持つことになります。 1つはフォルダ1(または2と3)のdev
1つが上書きされているため、その環境ファイルの最初の選択肢になります。 、他のファイルは上書きされませんでした。stage
prod
(画像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.gz
products-prod.gz
figure 2
figure 3
一部のLinuxコマンドでこれを実行できますか?唯一の混乱は、特定のサブフォルダ内の特定の環境ファイルをオーバーライドしてから、tar.gz
3つの異なるファイルを生成する方法です。
修正する:
また、次の点を考慮してください。
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
各環境固有のファイルに空のファイルを作成しようとしています。folder3
tar.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%'
)
指示:
- GNU ツールの構文を表示します。 BSDの場合は、justをjustに置き換える必要があり
find
ます(< - 最後を参照)。-regextype posix-extended
-E
tar
--no-recursion
-n
--transform=s
s
-s
- デモを簡素化するために、コードスニペットは含まれているディレクトリで実行されると仮定し、保持する「環境」ディレクトリ名のカスタム変数
Products
と名前を含む短い名前のヘルパー変数を使用します。$e
$r
Products
- コマンドラインから実行する
$r
と、シェルを汚染しないように括弧で囲み、サブシェルにします。$e
- 元のファイルをコピーしたりリンク/参照したり、有効なファイル名を処理したり、メモリ制限がなく、すべての名前を処理したりできます。唯一の仮定は、ディレクトリ階層の最初の2つのレベルです。 1レベル以下のディレクトリは「環境」ディレクトリと見なされるため無視されます(に示されているディレクトリを除く
$e
)。
for e in dev prod stage; do ...; done
シェルループにコードスニペットを含めるだけです。 (最も外側のブラケットを外し、ループfor
全体を囲むこともできます)。
良い点は、かなり短く、比較的簡単なことです。
欠点は、常に保持されることです。みんなこれ覆われたfind
ファイル(デフォルトファイルなど)の場合、デュアルコマンドは最初にtar
上書きするファイルを提供するため、抽出プロセス中にそのファイルがオーバーレイファイル(「環境」固有のファイルなど)で上書きされます。これにより、より大きなアーカイブの作成と抽出に時間がかかり、この「オーバーヘッド」が無視できるかどうかによっては望ましくない可能性があります。
エッセイで説明されているパイプラインは次のとおりです。
- (最も外側の括弧と補助変数を除く)
- 最初の
find
コマンドは非特定のファイルのリスト(および更新に応じたブートディレクトリ)のみを生成し、2番目のコマンドはすべてのfind
環境関連ファイルのリストのみを生成します。 - 両方のコマンド自体は括弧で囲まれ、出力が順番に
find
パイプに入ります。tar
tar
これらのパイプを読み取ってファイル名を取得し、そのファイルをアーカイブに保存すると同時に、各--transform
ファイルのパス名から「環境」コンポーネント(存在する場合)を削除して名前を変更します。- 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%'
)
参考にするいくつかの点があります。
- GNUおよびBSD構文について以前に述べた内容はすべてここに適用されます
find
。tar
- 以前のソリューションと同様に、ディレクトリ階層の最初の 2 つのレベルの前提には制約はありません。
sed
ここでは、NULLで区切られたI / O(オプション)を処理するためにGNUを使用していますが、これら2つのコマンドをシェルループ(Bashバージョン3以降が必要)または一意であると確信する他の言語に-z
簡単に置き換えることができます。推奨事項は次のとおりです。 NULLで区切られたI / Oを処理できるツールを使用する必要があります(たとえば、GNUでこれを実行できます)。 Bashループを使用した代替方法については、以下を参照してください。sed
while read ...
gawk
find
私は暗黙の行動に依存しないので、ここではシングルを使用しています。tar
- コマンドのパスを開くコマンドジョブ名の
sed
リストsort
- 特に最初のものは、
sed
「環境」の名前をパスの先頭に移動し、0
その前に補助番号を追加します。これは、非環境ファイルの前に並べ替えることを目的としています。なぜなら、私は後者の前にプレフィックスを追加したからです。 、1
目的はソートです - この準備では、「eyes」コマンドの名前リストを正規化し、
sort
すべての名前が「環境」名を使用せず、すべての名前の先頭に同じ数のスラッシュで区切られたフィールドを持つようにします。これはsort
キー定義にとって重要です。 - 最初のものは、
sort
最初にファイル名に基づいて並べ替えて同じ名前を並べてから、前のマークの数値をコマンドして、0
「環境」特定のファイル(存在する場合)が表示されることを保証することによって適用されます。非特異的対応物の隣に1
sed
- ファイル名の2番目の
sort
マージ(オプション)-u
は、最初の重複名のみを残します。これは、以前の並べ替えのために存在する場合は常に「環境」別のファイルです。 - 最後に、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 {}
)
sed
Bashループを使用して最初のコマンドを置き換える例:
(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
。