LoginSignup
12
23

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-01-01

目的:Powershellの正規表現と置換の習得

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

※書きはじめ2016/1/1 完了予定2016/1/3


Powershellに親しむことを目的としているため、寄り道、脱線もたくさんします


$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
#}
###



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




# やりたいこと
  # 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$&' }
  }
}


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

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

  # 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$&' }
  }
}


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

  # 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
  }





⇓⇓【追加要件】書き始め:1/3 12:20 完了予定:~1/3中

  • 検索したファイルがテキストの場合、中身を読み込んで処理する
    • 「:」をデリミタにして、1要素目と2要素目を取り出す
    • 正規表現を用いて置換を行う

    ⇒⇒【完了】2016/1/3 15:02 ⇓⇓




# 検索ドライブパス
$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}"
  }
12
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
23