Powershellの正規表現と置換の習得

# 目的：Powershellの正規表現と置換の習得
* 最終的に作るもの
  * C:\work\配下の様々なフォルダ階層にあるファイルを、C:\work2\配下にmoveして集める
     * move先でファイル名が重複した場合、「ファイル名_通番.拡張子」にリネームする 

##※書きはじめ2016/1/1　完了予定2016/1/3
</br>
</br>
####Powershellに親しむことを目的としているため、寄り道、脱線もたくさんします

```ps1

$FROM = "C:\work"
$MOVETO = "C:\work2"

#ディレクトリリスト取得
$DIR_ = dir ${FROM}
#ディレクトリリストからファイル名一覧取得
$FILENM = ${DIR_}.name
#ディレクトリリストからファイル名フルパス一覧取得
$FULLNM = ${DIR_}.fullname


# if文で、-match演算子を用い、正規表現による文字列検索 
foreach ($i in $FILENM) {

  # ^ = 右の文字が行頭になる位置条件を表す特殊文字
  # $ = 左の文字が行末になる位置条件を表す特殊文字

    # 例：文字列"yamamoto"「だけ」にマッチさせる場合
    # if ( $i -match ^yamamoto$){処理}


  # . = 任意の1文字を表す特殊文字
  # * = 繰り返しを表す特殊文字
  # \ = エスケープ文字
    # 「.txt」の場合は「\.txt」と書いて、特殊文字の意味を消す

    # 例：「.txt」の拡張子のファイル名すべてにマッチさせる場合
    # if ( $i -match ^.*\.txt$){処理}


  # \s = 空白の正規表現
  # \S = 空白以外の正規表現
  if ( ${i} -match "^.*\S\.txt$") {
  write ${i}
  }
}
    # 拡張子を除いた1番後ろの文字が空白の場合、マッチしない
      # "AAAAA.txt"にマッチし、"AAAA .txt"にはマッチしない
    # \sは反対
　    # "AAAA .txt"にマッチし、"AAAAA.txt"にはマッチしない




foreach ($i in $FILENM) {

  # \w = 単語
  # \W = 単語以外
  if ( "${i}" -match "^.*\W..txt$") {
  write ${i}
  }
}




foreach ($i in $FILENM) {

  # \b = 単語の境界
  # \B = 単語の境界ではないことを示す
  if ( "${i}" -match "^.*\b..txt$") {
  write ${i}
  }
}




foreach ($i in $FILENM) {


  # {n} = 直前の文字のn回の繰り返し このばあいは「.(任意の文字)」を3回
  if ( "${i}" -match "^.{3}.txt$") {
  write ${i}
  }
}
#ren $i ${i}_1.txt
#}
###



```
</br>

##ごちゃごちゃしてきたので作り直し


```ps1



# やりたいこと
  # C:\work\配下の様々なフォルダ階層にある全ファイルを、C:\work2\配下にmoveして集める
  # move先でファイル名が重複した場合、「ファイル名_通番.拡張子」にリネームする


# 検索ドライブパス
$DRIVE = "C:"
# 検索の最上位とするフォルダ　このフォルダ配下の全ファイルを再帰的に探します
$FROM = "\work"

# 検索したファイルの移動先
$MOVETO = "C:\work2"

# 検索
$DIR_ = dir -Recurse ${DRIVE}${FROM}
  # ファイル名一覧取得
  $FILENM = ${DIR_}.name
  # フルネームで取得
  $FULLNM = ${DIR_}.fullname




foreach ($i in $FULLNM) {

  # ファイル名にbとcを順不同に含む場合を検出してみる
  # if (($i -match "${DRIVE}\${FROM}(\\.*)*\\.*b.*c.*\.(.)*$") -or ($i -match "${DRIVE}\${FROM}(\\.*)*\\.*c.*b.*\.(.)*$")) {
  # write $i
  # 拡張子の前に「_1」とつけてリネームしてみる
  #  dir ${i} | rename-item -newname { $_.name -replace '\.(.)*','_1$&' }
  #}
  
  #上をコメントアウトしてswitch文で書いてみる
  switch -regex ($i) {
  "${DRIVE}\${FROM}(\\.*)*\\.*b.*c.*\.(.)*$" {
  write $i
  }
  "${DRIVE}\${FROM}(\\.*)*\\.*c.*b.*\.(.)*$" {
  write $i
  }
  }
  dir ${i} | rename-item -newname { $_.name -replace '\.(.)*','_1$&' }
  }
}


```
</br>

##ごちゃごちゃしてきたので作り直し２

* だいぶ惜しいところまできた

```ps1

  # C:\work\配下の様々なフォルダ階層にある全ファイルを、C:\work2\配下にmoveして集める
  # move先でファイル名が重複した場合、「ファイル名_通番.拡張子」にリネームする

# 検索ドライブパス
$DRIVE = "C:"
# 検索の最上位とするフォルダ　このフォルダ配下の全ファイルを再帰的に探します
$FROM = "\work"

# 検索したファイルの移動先
$MOVETO = "C:\work2"

# 検索
$DIR_ = dir -file -Recurse ${DRIVE}${FROM}
  # ファイル名一覧取得
  $FILENM = ${DIR_}.name
  # フルネームで取得
  $FULLNM = ${DIR_}.fullname

  # 重複ファイル名カウント用ハッシュ
  $count = @{}

foreach ($i in $FULLNM) {
$fullpass = $i
$filename = &{split-path "$i" -leaf}

$count.$filename += 1
$counter = $count.$filename

  if ("$counter" -ge 2) {
  dir ${i} | rename-item -newname { $_.name -replace '\.(.)*','_xxxx$&' }
  }
}


```
</br>

#⇓⇓【成功】2016/1/2 21:14　完了

</br>


```ps1
  # C:\work\配下の様々なフォルダ階層にある全ファイルを、C:\work2\配下にmoveして集める
  # move先でファイル名が重複した場合、「ファイル名_通番.拡張子」にリネームする


# 検索ドライブパス
$DRIVE = "C:"

# 検索の最上位とするフォルダ　このフォルダ配下の全ファイルを再帰的に探します
$FROM = "\work"

# 検索したファイルの移動先
$MOVETO = "C:\work2"




# ファイル一覧を取得する

  # DIR結果を変数格納
  $DIR_ = dir -file -Recurse ${DRIVE}${FROM}

  # DIRからファイル名一覧を取得
  $FILENM = ${DIR_}.name

  # DIRからフルパス(X:\xxxx\xxxx.xxxx)で取得　
  $FULLNM = ${DIR_}.fullname




# ファイル一覧から、重複したファイル名をリネームする
  # ファイル名カウント用に空ハッシュ作成
  $count = @{}

  # フルパス(X:\xxxx\xxxx.xxxx)で取得した全階層にあるファイル一覧を、1行ずつ読み込み
  foreach ($i in $FULLNM) {

    # ファイル名を変数格納
    $filename = &{split-path "$i" -leaf}

    # ファイル名を拡張子なしで変数格納
    $noextent = [System.IO.Path]::GetFileNameWithoutExtension($i)

    # 拡張子のみを変数格納
    $extent = [System.IO.Path]::GetExtension($i)


    # ファイル名1つをキーにして、値を1カウントアップ
    $count.$filename += 1

    # カウントアップした数を変数格納
    $counter = ${count}.${filename}

    # 重複したファイル名は、カウントアップした数字が2以上になる
    if ("$counter" -ge 2) {

      # カウントアップした数字をファイル名に付番しリネーム
      # ファイル名(フルパス) → ファイル名(拡張子なし)_カウントアップした数_拡張子
      ren ${i} -newname ${noextent}_${counter}_${extent}

    } 
  }




# リネームした後にファイル一覧を再取得する

  # DIR結果を変数格納
  $DIR2_ = dir -file -Recurse ${DRIVE}${FROM}

  # DIRからファイル名一覧を取得
  $FILENM2 = ${DIR2_}.name

  # DIRからフルパス(X:\xxxx\xxxx.xxxx)で取得　
  $FULLNM2 = ${DIR2_}.fullname




# moveする
  foreach ($i in $FULLNM2) {
  mv -Path $i -Destination $MOVETO
  }





```
</br>

#⇓⇓【追加要件】書き始め：1/3 12:20　完了予定：～1/3中
* 検索したファイルがテキストの場合、中身を読み込んで処理する
 * 「:」をデリミタにして、1要素目と2要素目を取り出す
 * 正規表現を用いて置換を行う

#　　　　⇒⇒【完了】2016/1/3 15:02　⇓⇓
</br>

```ps1



# 検索ドライブパス
$DRIVE = "C:"

# 検索の最上位とするフォルダ　このフォルダ配下の全ファイルを再帰的に探します
$FROM = "\work"

# 検索したファイルの移動先
$MOVETO = "C:\work2"




# ファイル一覧を取得する

  # DIR結果を変数格納
  $DIR_ = dir -file -Recurse ${DRIVE}${FROM}

  # DIRからファイル名一覧を取得
  $FILENM = ${DIR_}.name

  # DIRからフルパス(X:\xxxx\xxxx.xxxx)で取得　
  $FULLNM = ${DIR_}.fullname



# ファイルの中身にある"ccc"という文字列を空白("")に置換する(1/3追加)
  foreach ($i in $FULLNM2){

  # Get-Content(gc)で読み込んだ1行目($i)のファイルを開く
  $readfile = &{gc $i}

  # 正規表現で置換
  # "ccc"は正規表現で"\bccc\b"　(\bは単語の境界)
  # ※エスケープシーケンスは不要　(Perlでいうところの\Q"string"\E)
    ## つまり、"ccbc"などは、検索結果に含まれず、純粋な"ccc"が置換対象となる
  $renamed = $readfile -replace "\bccc\b",""

  # 置換結果をファイルに上書きする
  $renamed >$i
  }

  
  # 「:」をデリミタにして区切る
  foreach ($i in ${FULLNM}){

  # Get-Content(gc)で読み込んだ1行目($i)のファイルを開く
  $readfile = &{gc $i}

  # split関数に渡して「:」で区切る
  $nocolon = $readfile.Split(":")

    # ファイル名を拡張子なしで変数格納
    $noextent = [System.IO.Path]::GetFileNameWithoutExtension($i)

    # 拡張子のみを変数格納
    $extent = [System.IO.Path]::GetExtension($i)

    # ファイル名をパスのみ取得
    $pass_ = [System.IO.Path]::GetDirectoryName($i)

  # 保存：元のファイルパス\元のファイル名_nocolon_拡張子
  $nocolon >"${pass_}\${noextent}_nocolon_${extent}"
  }