PowerShellで、階層コピー

階層情報を保持したまま、単一ファイルをコピーする。

いろんなサイトでやっている、PowerShellによる階層コピーにチャレンジしてみた。これができれば、結構いろんなことができる。車輪の再発明

実行前

このような状態からtest1の中にある3.txtだけを、階層を保ったまま、test2コピーするのが目標。 結果、test2\hoge\hogehoge\hogehogehoge\3.txtを作りたい。 higehigeやhagehagehageは作らない。

E:\
├─test1
│  └─hoge
│      ├─higehige
│      └─hogehoge
│          ├─hagehagehage
│          │      9.txt
│          │
│          └─hogehogehoge
│                  1.txt
│                  2.txt
│                  3.txt
│                  4.txt
│
└─test2
想定結果

この様な形になって欲しい。

E:\
├─test1
│  └─hoge
│      ├─higehige
│      └─hogehoge
│          ├─hagehagehage
│          │      9.txt
│          │
│          └─hogehogehoge
│                  1.txt
│                  2.txt
│                  3.txt
│                  4.txt
│
└─test2
    └─hoge
        └─hogehoge
            └─hogehogehoge
                    3.txt
実行スクリプト

というわけでスクリプトを書いてみた。

$src_dir = "E:\test1"
$dest_dir = "E:\test2"

$src_file = "\hoge\hogehoge\hogehogehoge\3.txt"

#パスを結合する。
$src = Join-Path $src_dir $src_file
$dest = Join-Path $dest_dir $src_file
echo "コピー元:$src"

#対象がファイルであれば処理を実行する。
if(Test-Path $src -PathType Leaf){
    #転送先の親ディレクトリパスを取得
    $dir = Split-Path $dest -Parent
    echo "コピー先:$dir"
    #ディレクトリが無ければ作成する。
    if(!(Test-Path $dir -PathType Container)){
        #何階層だろうと一気に作れるみたい。
        New-Item $dir -itemType Directory
    }
    echo "ファイルをコピーします。:$src ==> $dir"
    #ファイルが既にあっても上書きコピー
    Copy-Item $src $dir -Force
}

「New-Item ~ -itemType Directory」で、深い階層まで一気に作れるのがわかったで助かった。最初はパスを分解して一つずつフォルダを作るつもりだったので。

PowerShellで、ファイル一覧を取得する。

フルパスのファイル一覧を取得する。

Get-ChildItem -Recurse -file -Exclude .lnk | Sort-Object FullName |ForEach-Object{$_.FullName}

フルパス or 相対パスのファイル一覧をファイル出力する。

コメントのところで相対パス絶対パスを切り替える。

Get-ChildItem -Recurse -file -Exclude .lnk | ForEach-Object {
#$_.FullName
Resolve-Path -Relative $_.FullName
} | Out-File -FilePath D:\hoge.txt

ところで、Resolve-Path使うと行数ががっさりと減っちゃうんだけど、パスの中に"["とか"]"が入っていると消えちゃうみたい。ワイルドカード扱いされて結果無しになっちゃう。"["か"]"のどっちかだとコンバートエラーになる。絶対パス==>相対パスの切り替えにResolve-Pathを使うのはやめた方がよさそう。んで、書き換え。

Get-ChildItem -Recurse -file -Exclude .lnk | ForEach-Object {
#$_.FullName
$_.FullName.Replace($path, ".\")
} | Out-File -FilePath D:\hoge.txt

下手に正規表現とか使わずに、シンプルにいった方がよさそう。Windowsでやるなら頭にドライブ名が入るので、行頭からの一致とか気にする必要はないと思うけど、Linuxの場合は行頭からの一致に絞り込む必要はあると思う。

とりあえずのバックアップ設定。(PowerShellでrobocopy起動)

# コピー元のPATH
$basePath = "D:"
# コピー先のPATH
$destPath = "H:"
# ログファイル名(フルパス)
$logFile = $basePash + "\robocopy.log"


# /E :: 空のディレクトリを含むサブディレクトリをコピーします。
# /COPY:コピーフラグ :: ファイルにコピーする情報 (既定値は /COPY:DAT)。
#                       (コピーフラグ: D= データ、A= 属性、T= タイムスタンプ)。
#                       (S= セキュリティ =NTFS ACL、O= 所有者情報、U= 監査情報)
# /DCOPY:コピーフラグ :: ディレクトリにコピーする情報 (既定値は /DCOPY:DA)。
#                       (コピーフラグ: D= データ、A= 属性、T= タイムスタンプ)。
# /XF file [ファイル]... :: 指定された名前/パス/ワイルドカードに一致するファイルを除外します。
# /XJ :: 接合ポイントとシンボリック リンクを除外します (通常は既定で含まれます)。
# /R:n :: 失敗したコピーに対する再試行数: 既定値は 1,000,000。
# /V :: スキップされたファイルを示す詳細出力を作成します。
# /TS :: 出力にコピー元ファイルのタイム スタンプを含めます。
# /FP :: 出力にファイルの完全なパス名を含めます。
# /BYTES :: サイズをバイトで出力します。
# /LOG+:ファイル :: ログ ファイルに状態を出力します (既存のログ ファイルに追加します)。
# /TEE :: コンソール ウィンドウとログ ファイルに出力します。

robocopy $basePath $destPath /E /COPY:DAT /DCOPY:DAT /XF $logFile /XO /XJ /R:2 /W:5 /V /TS /BYTES /LOG+:$logFile /TEE

続・PhowerShellでフォルダ内のファイルハッシュを取得する

以前PowerShellでフォルダ内のファイルのハッシュ値を取得しましたが。 PhowerShellでフォルダ内のファイルハッシュを取得する - キジモナカズバ

いやいや。自分でForEach-Objectとかで回さんでもソートしてからExport-Csvでタブ区切りできるやん。ってやってみました。

$algorithm="SHA512"
$delimtter="`t"
$encoding="UTF8"

do{
$path = Read-Host "対象パスを入力してください"
}while(!(Test-Path $path) )

cd $path
$here = Convert-Path .
$file = Join-Path (Split-Path -Parent $here) ($algorithm + "_" + (Split-Path -Leaf $here) + ".txt")

echo $file

$hash = Get-ChildItem -File -Recurs | Get-FileHash -Algorithm $algorithm | Sort-Object Path | Export-Csv  -Delimiter $delimtter -Encoding $encoding -Path $file

アルゴリズムは変数に浮かしてSHA512とか使ってます。 Pathが絶対パスになっちゃってるのはどうしようもないのかな…。 Export-CSV -Confirmオプション付けて確認ダイアログ出してみたら、ボタン多すぎてわけわからなんかったのでやめた。 あとは進捗的なの出そうと思ったらForEach-Object使った方がやりやすそうね。

Javaの可変長引数を色々。

可変長引数色々。結局は配列になる。

お試しコード

public class Main {
    public static void main(String[] args) throws Exception {
        // Your code here!
        Main main = new Main();
        main.hoge("hoge","hige","hage");
    }
    private void hoge(String ... hoges){
        System.out.println(hoges.getClass().getName());
        for(int i = 0; i < hoges.length; i++){
            System.out.println(hoges[i]);
        }
        for(String ho: hoges){
            System.out.println(ho);
        }
    }
}

実行結果

[Ljava.lang.String;
hoge
hige
hage
hoge
hige
hage

参考

https://paiza.io/projects/266GxsDaHS0ypsxjC1m8fw?locale=en-us

PhowerShellでフォルダ内のファイルハッシュを取得する

はい。ズバリどん。
ISEのスクリプトウィンドウの方に以下貼り付け。

do{
$path = Read-Host "対象パスを入力してください"
}while(!(Test-Path $path) )

cd $path
$here = Convert-Path .
$file = Join-Path (Split-Path -Parent $here) ("md5_" + (Split-Path -Leaf $here) + ".txt")
echo $file
$hash = Get-ChildItem -File -Recurs | Get-FileHash -Algorithm MD5 | Sort-Object Path
$hash | foreach {
$_.Algorithm + "," + $_.Hash + "," + $_.Path.Replace($here,".")
#$_.Algorithm + "`t" + $_.Hash + "`t" + (Resolve-Path -Relative $_.Path)
} | Out-File -FilePath $file

結構いろいろやっていて、

  1. 対象パスを入力させる。
  2. 作業フォルダ以下にあるすべてのファイルのmd5ハッシュを再帰的に取得する。
  3. ファイルのフルパス名順に並べ替える。
  4. ハッシュ取得結果のフルパスを作業フォルダからの相対パスに置換する
  5. タブ区切りな。(スペースとかカンマとかあんま好かんす)
  6. 作業フォルダ名をもじったファイル名を作成しそこに出力

絶対パス相対パスに置き換えるとこ、最初は置換でやってたけど専用コマンドレットに置き換えてコメントアウトResolve-Pathだとパスに"["や"]"が含まれるとワイルドカード扱いされて正しく動作しないので、Replaceに戻す。(2019-08-10)