データ整形作業が煩わしいと感じたことはありませんか?
この記事では、バッチファイルとCygwinのawkスクリプトを組み合わせ、数行のコードで、以下の例に示す複雑なデータ整形をPowerQueryを使わずに手軽に実現可能とする驚くべき方法を紹介します。
この記事ではCygwinの基本から始め、awkスクリプトの作り方、バッチファイルと Cygwinの awk スクリプトを組み合わせ方まで順にご紹介しますので興味のある方はぜひ最後までご覧下さい。
目次
Cygwinとは?
Cygwinは、Windows環境でUNIX/Linux風のコマンドや環境を使用できるようにするツールです。
このCygwinの中には、Windows上にUNIX互換のオペレーティングシステムをエミュレートするためのライブラリ「cygwin1.dll」や、Windows環境で動作するようにコンパイルされた多数のツールが含まれており
これにより、bash、sed、grep、awkなど、通常UNIX/Linux環境で利用される便利なツールを、Windows環境のターミナル画面「Cygwin64 Terminal」からも利用することができるようになっています。
Cygwin のインストール方法、基本的なコマンド、そして標準入出力・リダイレクション・パイプといった UNIX/Linux 環境下でコマンドをより便利に使うための重要な考え方については以下のような素晴らしい記事が公開されていますので必要に応じてこれらの記事を参照頂ください。
具体的には以下のWindowsの「Cygwin64 Terminal」から起動される
次のターミナル画面から bash、sed、grep、awkといった通常 UNIX/Linux で利用可能な便利なツールを Windows 環境下の下記ターミナル画面から実行します。
Cygwinの実行パスを確認しよう
さてこのCygwin の実行パスはどこにあるのでしょうか。
実はこの実行パスは他の Windows 10 で使用可能なアプリケーションの実行パスの求め方と同様に求めることができます。
つまり下記のように
Windows メニュー > Cygwin > Cygwin64 Terminal > その他 > ファイルの場所を開く
を順番に選択していき、以下のように Cygwin64 Terminal へのショートカットキーへたどり着いた後に
このショートカットキーをマウスで右クリックして コンテキストメニューを表示し、1番下の プロパティ を選択すると次のような画面が出ます。
このリンク先(T): に記入された絶対パスの先頭部分 C:\cygwin64\bin が今まで見てきた画面に登場するフォルダの絶対パス名です。
コマンドライン画面からCygwinを操作する方法
実はWindowsのコマンドラインでCygwinがインストールされているディレクトリへ移動したり、そのディレクトリの実行PATHを設定した後は、Windowsのコマンドラインやバッチファイルから、直接Linuxのコマンドを指定して実行することも可能です。
Cygwinでどんなコマンドが使えるかは先ほどの
C:\cygwin64\bin
配下で “ls *.exe” コマンドを入力すると以下のように確認できます。
つまりこのフォルダ下で .exe という拡張子がついているものが Cygwin で使用可能なコマンドです(Cygwin 配下では .exe の指定は不要)。
Microsoft Windows [Version 10.0.19045.3324]
(c) Microsoft Corporation. All rights reserved.
C:\Users\devel.RVH>cd \cygwin64\bin
C:\cygwin64\bin>ls *.exe
'[.exe' env.exe ld.bfd.exe pic.exe tac.exe
addftinfo.exe eqn.exe ld.exe pinky.exe tail.exe
addr2line.exe expand.exe ldd.exe pldd.exe tar.exe
ar.exe expr.exe ldh.exe post-grohtml.exe taskset.exe
arch.exe factor.exe less.exe pr.exe tbl.exe
as.exe false.exe lessecho.exe pre-grohtml.exe tee.exe
ash.exe file.exe lesskey.exe preconv.exe test.exe
b2sum.exe find.exe lexgrog.exe printenv.exe testlibusb-win.exe
base32.exe flock.exe link.exe printf.exe testlibusb.exe
base64.exe fmt.exe lkbib.exe profiler.exe tfmtodit.exe
basename.exe fold.exe ln.exe ps.exe tic.exe
bash.exe gawk-5.1.1.exe locale.exe ptx.exe timeout.exe
bunzip2.exe gawk.exe locate.exe pwd.exe toe.exe
bzcat.exe gcc-ar.exe logger.exe pzstd.exe touch.exe
bzip2.exe gcc-nm.exe login.exe ranlib.exe tput.exe
bzip2recover.exe gcc-ranlib.exe logname.exe readelf.exe tr.exe
c++filt.exe gcc.exe look.exe readlink.exe troff.exe
cal.exe gcov-dump.exe lookbib.exe readshortcut.exe true.exe
cat.exe gcov-tool.exe ls.exe realpath.exe truncate.exe
catman.exe gcov.exe lsattr.exe rebase.exe trust.exe
chattr.exe gencat.exe lto-dump.exe refer.exe tset.exe
chcon.exe getconf.exe lzmadec.exe regtool.exe tsort.exe
chgrp.exe getent.exe lzmainfo.exe rename.exe tty.exe
chmod.exe getfacl.exe make.exe renice.exe tzset.exe
chown.exe getopt.exe man-recode.exe rev.exe ul.exe
chroot.exe gkill.exe man.exe rm.exe umount.exe
chrt.exe gmondump.exe mandb.exe rmdir.exe uname.exe
cksum.exe gpg.exe manpath.exe run.exe unexpand.exe
clear.exe gpgsplit.exe mcookie.exe runcon.exe uniq.exe
cmp.exe gpgv.exe md5sum.exe script.exe unlink.exe
col.exe gprof.exe minidumper.exe scriptreplay.exe users.exe
colcrt.exe grep.exe mintty.exe sdiff.exe uuidgen.exe
colrm.exe grn.exe mkdir.exe sed.exe uuidparse.exe
column.exe grodvi.exe mkfifo.exe seq.exe vdir.exe
comm.exe groff.exe mkgroup.exe setfacl.exe vi.exe
cp.exe grolbp.exe mknod.exe setmetamode.exe vim.exe
cpp.exe grolj4.exe mkpasswd.exe setsid.exe wc.exe
csplit.exe grops.exe mkshortcut.exe sh.exe wget.exe
curl.exe grotty.exe mktemp.exe sha1sum.exe whatis.exe
cut.exe groups.exe more.exe sha224sum.exe whereis.exe
cygcheck.exe gzip.exe mount.exe sha256sum.exe which.exe
cygpath.exe head.exe mv.exe sha384sum.exe who.exe
cygstart.exe hexdump.exe namei.exe sha512sum.exe whoami.exe
cygwin-console-helper.exe hostid.exe nice.exe shred.exe windmc.exe
dash.exe hostname.exe nl.exe shuf.exe windres.exe
date.exe hpftodit.exe nm.exe size.exe x86_64-pc-cygwin-gcc-11.exe
dd.exe id.exe nohup.exe sleep.exe x86_64-pc-cygwin-gcc-ar.exe
df.exe indxbib.exe nproc.exe soelim.exe x86_64-pc-cygwin-gcc-nm.exe
diff.exe info.exe numfmt.exe sort.exe x86_64-pc-cygwin-gcc-ranlib.exe
diff3.exe infocmp.exe objcopy.exe split.exe x86_64-pc-cygwin-gcc.exe
dir.exe install-info.exe objdump.exe ssp.exe xargs.exe
dircolors.exe install.exe od.exe stat.exe xmlcatalog.exe
dirname.exe ipcmk.exe openssl.exe stdbuf.exe xmllint.exe
dlltool.exe ipcrm.exe p11-kit.exe strace.exe xxd.exe
dllwrap.exe ipcs.exe passwd.exe strings.exe xz.exe
du.exe isosize.exe paste.exe strip.exe xzdec.exe
dumper.exe join.exe pathchk.exe stty.exe yes.exe
echo.exe jq.exe peflags.exe sum.exe zdump.exe
editrights.exe kill.exe pfbtops.exe sync.exe zstd.exe
elfedit.exe lastlog.exe pg.exe tabs.exe
C:\cygwin64\bin>
この “ls *.exe” はLinux の代表的なコマンドの一つで、 Windows のコマンドラインにおける dir *.exe に相当するコマンドです。
Cygwin64 Terminalでの操作手順
では同じことを Cygwin64 Terminal でも実行してみましょう。
devel@rvhsv01 ~
$ cd '\cygwin64\bin'
devel@rvhsv01 /usr/bin
$ ls *.exe
'[.exe' gzip.exe refer.exe
addftinfo.exe head.exe regtool.exe
addr2line.exe hexdump.exe rename.exe
ar.exe hostid.exe renice.exe
arch.exe hostname.exe rev.exe
as.exe hpftodit.exe rm.exe
ash.exe id.exe rmdir.exe
b2sum.exe indxbib.exe run.exe
base32.exe info.exe runcon.exe
base64.exe infocmp.exe script.exe
basename.exe install-info.exe scriptreplay.exe
bash.exe install.exe sdiff.exe
bunzip2.exe ipcmk.exe sed.exe
bzcat.exe ipcrm.exe seq.exe
bzip2.exe ipcs.exe setfacl.exe
bzip2recover.exe isosize.exe setmetamode.exe
c++filt.exe join.exe setsid.exe
cal.exe jq.exe sh.exe
cat.exe kill.exe sha1sum.exe
catman.exe lastlog.exe sha224sum.exe
chattr.exe ld.bfd.exe sha256sum.exe
chcon.exe ld.exe sha384sum.exe
chgrp.exe ldd.exe sha512sum.exe
chmod.exe ldh.exe shred.exe
chown.exe less.exe shuf.exe
chroot.exe lessecho.exe size.exe
chrt.exe lesskey.exe sleep.exe
cksum.exe lexgrog.exe soelim.exe
clear.exe link.exe sort.exe
cmp.exe lkbib.exe split.exe
col.exe ln.exe ssp.exe
colcrt.exe locale.exe stat.exe
colrm.exe locate.exe stdbuf.exe
column.exe logger.exe strace.exe
comm.exe login.exe strings.exe
cp.exe logname.exe strip.exe
cpp.exe look.exe stty.exe
csplit.exe lookbib.exe sum.exe
curl.exe ls.exe sync.exe
cut.exe lsattr.exe tabs.exe
cygcheck.exe lto-dump.exe tac.exe
cygpath.exe lzmadec.exe tail.exe
cygstart.exe lzmainfo.exe tar.exe
cygwin-console-helper.exe make.exe taskset.exe
dash.exe man-recode.exe tbl.exe
date.exe man.exe tee.exe
dd.exe mandb.exe test.exe
df.exe manpath.exe testlibusb-win.exe
diff.exe mcookie.exe testlibusb.exe
diff3.exe md5sum.exe tfmtodit.exe
dir.exe minidumper.exe tic.exe
dircolors.exe mintty.exe timeout.exe
dirname.exe mkdir.exe toe.exe
dlltool.exe mkfifo.exe touch.exe
dllwrap.exe mkgroup.exe tput.exe
du.exe mknod.exe tr.exe
dumper.exe mkpasswd.exe troff.exe
echo.exe mkshortcut.exe true.exe
editrights.exe mktemp.exe truncate.exe
elfedit.exe more.exe trust.exe
env.exe mount.exe tset.exe
eqn.exe mv.exe tsort.exe
expand.exe namei.exe tty.exe
expr.exe nice.exe tzset.exe
factor.exe nl.exe ul.exe
false.exe nm.exe umount.exe
file.exe nohup.exe uname.exe
find.exe nproc.exe unexpand.exe
flock.exe numfmt.exe uniq.exe
fmt.exe objcopy.exe unlink.exe
fold.exe objdump.exe users.exe
gawk-5.1.1.exe od.exe uuidgen.exe
gawk.exe openssl.exe uuidparse.exe
gcc-ar.exe p11-kit.exe vdir.exe
gcc-nm.exe passwd.exe vi.exe
gcc-ranlib.exe paste.exe vim.exe
gcc.exe pathchk.exe wc.exe
gcov-dump.exe peflags.exe wget.exe
gcov-tool.exe pfbtops.exe whatis.exe
gcov.exe pg.exe whereis.exe
gencat.exe pic.exe which.exe
getconf.exe pinky.exe who.exe
getent.exe pldd.exe whoami.exe
getfacl.exe post-grohtml.exe windmc.exe
getopt.exe pr.exe windres.exe
gkill.exe pre-grohtml.exe x86_64-pc-cygwin-gcc-11.exe
gmondump.exe preconv.exe x86_64-pc-cygwin-gcc-ar.exe
gpg.exe printenv.exe x86_64-pc-cygwin-gcc-nm.exe
gpgsplit.exe printf.exe x86_64-pc-cygwin-gcc-ranlib.exe
gpgv.exe profiler.exe x86_64-pc-cygwin-gcc.exe
gprof.exe ps.exe xargs.exe
grep.exe ptx.exe xmlcatalog.exe
grn.exe pwd.exe xmllint.exe
grodvi.exe pzstd.exe xxd.exe
groff.exe ranlib.exe xz.exe
grolbp.exe readelf.exe xzdec.exe
grolj4.exe readlink.exe yes.exe
grops.exe readshortcut.exe zdump.exe
grotty.exe realpath.exe zstd.exe
groups.exe rebase.exe
devel@rvhsv01 /usr/bin
$
さてここで一番最初に入力した
$ cd '\cygwin64\bin'
というコマンドに注目してみましょう。
この2つの ’ ’ (シングルクオーテーション)で囲んでいる中身の部分の表現は Windows のコマンドラインで使用しているフルパスの表記法と同じです。
つまり Cygwin では Windows のコマンドライン、エクスプローラで使用しているフルパス表現を2つの ’ ’ (シングルクオーテーション)で囲むことでそのまま使用することができます。
参考の為に同じことを Windows 10 内の WSL2 機能を使ってインストールした正式なLinux、つまり Ubuntu 22.04.2 でもやってみると
devel@rvhsv01:~$ cd '\cygwin64\bin'
-bash: cd: \cygwin64\bin: No such file or directory
残念ながら Ubuntu 22.04.2 では Cygwin内で許容された
cd 'c:\cygwin64\bin'
という様な Windowsのフルパス記述は許容されていません。
代わりに同ディレクトリの Linux としての完全なパス名である
cd /mnt/c/cygwin64/bin
というフルパスの入力(この /mnt/c という表現は Linux のOS にマウントされているドライブの中の C: ドライブのことを指します)をします。
あるいは wsl 環境でWindows環境、Linux環境間でのパスを取得する wslpath コマンドを使って
devel@rvhsv01:~$ echo $(wslpath -u 'c:\cygwin64\bin')
/mnt/c/cygwin64/bin
devel@rvhsv01:~$ echo $(wslpath -w '/mnt/c/cygwin64/bin')
C:\cygwin64\bin
あるいは
devel@rvhsv01:~$ echo `wslpath -u 'c:\cygwin64\bin'`
/mnt/c/cygwin64/bin
devel@rvhsv01:~$ echo `wslpath -w '/mnt/c/cygwin64/bin'`
C:\cygwin64\bin
を使ってcd コマンドのパス名を求め
devel@rvhsv01:~$ cd `wslpath -u 'c:\cygwin64\bin'`
devel@rvhsv01:/mnt/c/cygwin64/bin$
といった書き方をします。この $()あるいは2つの“(バッククォート)はその中に囲まれたコマンドを実行した結果の文字列を使うという意味です。
こういった方法を使えばUbuntuでもWindows のフルパス名から Ubuntsu 内のフルパス名を求めることが可能ですが Cygwin でのフォルダ名を2つの ’ ’ (シングルクオーテーション)で囲むやり方の方が直観的でわかりやすいですよね。
そこでしらかば堂はバッチファイルから Linux コマンドを利用したいときは Ubuntu ではなく Cygwin を利用しています。
ところで Cygwin にも Ubuntu 同様 Cygwin 環境でWindows環境、Linux環境間でパスを取得する cygpath コマンドというものが準備されており
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ cygpath -w "/usr/local/bin"
C:\cygwin64\usr\local\bin
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ cygpath -u "C:\cygwin64\usr\local\bin"
/usr/local/bin
この cygpath コマンドと$()を使って作業フォルダを変更したり、
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ echo $(cygpath -u "C:\cygwin64\usr\local\bin")
/usr/local/bin
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ cd $(cygpath -u "C:\cygwin64\usr\local\bin")
devel@rvhsv01 /usr/local/bin
$
あるいは cygpath コマンドと2つの “(バッククォート)を使って作業フォルダを変更することができます。
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ echo `cygpath -u "C:\cygwin64\usr\local\bin"`
/usr/local/bin
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ cd $(cygpath -u "C:\cygwin64\usr\local\bin")
devel@rvhsv01 /usr/local/bin
$ cd 'C:\cygwin64\usr\local\bin'
devel@rvhsv01 /usr/local/bin
$
ここに見るようにCygwinからWindowsのフォルダを指定する場合
cd $(cygpath -u "C:\cygwin64\usr\local\bin")
と
cd 'C:\cygwin64\usr\local\bin'
はほぼ同じ効果をもたらしますが、Cygwinのbash からWindowsのパスで指定したシェルスクリプトを起動する際は2つの’ ’ (シングルクオーテーション)で囲んだパスでは何故か起動できず、$()で囲んだパスのみで起動できるという差分があります。
なお Linux/UNIX環境下のシェルスクリプトにおける, ”, “”, \, “, $()の使用方法について @HAHOHIHOHU さんが素晴らしい記事を書かれているので必要に応じてそちらも必要に応じて参照くださいね。
パワフルなスクリプト言語awk
サンプルデータでawkの力を体感しよう
さて Cygwin 配下で利用可能なスクリプト言語 awk の便利さを体感するため
なんちゃって個人情報 にて500件ほどデータを作成し、これを文字コードutf-8 のcsv形式で
%USERPROFILE%\Downloads\dummy.csv
へ保存し、これを Cygwin Terminal で出力してみましょう。
%USERPROFILE% については必要に応じて以下の関連記事も参照下さい。
まずWindows上での絶対パスを確認するために、一旦Windowsのコマンドプロンプト内で上記のフォルダへ cd コマンドで移動します。
Microsoft Windows [Version 10.0.19045.3324]
(c) Microsoft Corporation. All rights reserved.
C:\Users\devel.RVH>cd %USERPROFILE%\Downloads
C:\Users\devel.RVH\Downloads>
C:\Users\devel.RVH\Downloads>dir d*.csv
ドライブ C のボリューム ラベルがありません。
ボリューム シリアル番号は 2485-30D8 です
C:\Users\devel.RVH\Downloads のディレクトリ
2023/08/18 22:42 74,559 dummy.csv
1 個のファイル 74,559 バイト
0 個のディレクトリ 281,937,391,616 バイトの空き領域
C:\Users\devel.RVH\Downloads>
このコマンド結果から上記の絶対パス名が
C:\Users\devel.RVH\Downloads
ファイル名が
dummy.csv
であることがわかりました。
今度はこれを Cygwin64 Terminal で参照し、csvファイルの中身を確認してみましょう。
$ cd 'C:\Users\devel.RVH\Downloads'
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Downloads
$ ls d*.csv
dummy.csv
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Downloads
$ wc d*.csv
501 1596 74559 dummy.csv
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Downloads
$ head d*.csv
名前,ふりがな,アドレス,性別,年齢,誕生日,婚姻,都道府県,携帯,キャリア,カレーの食べ方
越智 たまき,おち たまき,ochi_tamaki@example.com,女,40,1982/11/6,既婚,群馬県,090-7685-7025,au,右ルー・せき止め派
川添 佳乃,かわぞえ よしの,kawazoe_yoshino@example.com,女,21,2002/5/31,未婚,熊本県,090-7883-1303,ドコモ,ぶっかけ・別口派
毛利 莉緒,もうり りお,mouri_rio@example.com,女,61,1962/8/2,既婚,神奈川県,090- 132-3456,ツーカー,左ルー・せき止め派
大山 光,おおやま ひかる,ooyama_hikaru@example.com,男,26,1997/1/27,既婚,神奈川県,080-9974-2396,ソフトバンク,奥ルー・せき止め派
五味 圭,ごみ けい,gomi_kei@example.com,男,63,1960/8/14,既婚,兵庫県,080-1923-7247,au,ぶっかけ・混ぜ混ぜ派
秋葉 信吾,あきば しんご,akiba_shingo@example.com,男,48,1975/2/28,既婚,秋田県,080- 241-7694,ドコモ,左ルー・せき止め派
平田 憲史,ひらた のりひと,hirata_norihito@example.com,男,53,1969/9/3,既婚,東京都,080-1106-5699,au,右ルー・混ぜ混ぜ派
柳沢 和之,やなぎさわ かずゆき,yanagisawa_kazuyuki@example.com,男,56,1966/11/18,既婚,岡山県,080-1425-4206,ドコモ,左ルー・せき止め派
平川 芽以,ひらかわ めい,hirakawa_mei@example.com,女,24,1998/10/6,既婚,長野県,080-3727-1124,ドコモ,ぶっかけ・ルー攻め派
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Downloads
$
ここでの操作の内容を簡単に紹介すると以下のようになります。
行数 | コマンド | 意味 |
---|---|---|
1 | cd ‘C:\Users\devel.RVH\Downloads’ | Windows コマンドラインで確認したパスを使ってCygwin の中でフォルダ(ディレクトリ)移動します。その結果このフォルダはCygwinの中では /cygdrive/c/Users/devel.RVH/Downloads と表現されることがわかります。 |
4 | ls d*.csv | このフォルダに中に目的のファイル dummy.csv が存在するかを ワイルドカード * 付きの ls コマンドで確認しています。 |
8 | wc d*.csv | 念のためこの dummy.csv の行数(、単語数、文字数)を wc コマンドで確認しました。 なんちゃって個人情報 では件数を500件と指定して作成しているので、表題部も入れて501行あります。 |
12 | head d*.csv | 全部表示するとえらいことになるので、 head コマンドで最初の表題部も含めた10行分を表示しています。 |
この実行結果から、この dummy.csv が次のようなフィールド構成を持ったcsvファイルであることがわかりますね。
フィールド位置 | フィールド名 |
---|---|
1 | 名前 |
2 | ふりがな |
3 | アドレス |
4 | 性別 |
5 | 年齢 |
6 | 誕生日 |
7 | 婚姻 |
8 | 都道府県 |
9 | 携帯 |
10 | キャリア |
11 | カレーの食べ方 |
条件付きレコードとフィールドの抽出をマスターするawkの使い方
さて、ここからがこの記事の真骨頂です。
まずは以下をご覧ください。
これは dummy.csv から$4[つまり4番目の性別フィールド]が”男” かつ $8[つまり8番目の都道府県フィールド]が“東京都”という条件を満たすレコードのみを対象に、”,” をフィールドのセパレータとして指定した時の$1[つまり1番目の名前フィールド]と$9[つまり9番目の携帯フィールド]のみを”,”で区切って表示するawkコマンド(スクリプト言語)とその実行結果です。
$ cat dummy.csv | awk -F , '$4=="男" && $8=="東京都" { printf("%s,%s\n", $1,$9) }'
平田 憲史,080-1106-5699
原口 長利,080-3334-3271
大和 隆,090- 981-6868
細山 正義,080-7782-3514
根岸 豊,090-6475-2324
今田 光臣,090-9669-3186
池田 憲一,090-7560-9114
角谷 聡,080-4061-1381
水谷 優一,080-2592-6348
村上 洋,080-2751-6213
皆川 正敏,090-9582-6568
岡田 ケンイチ,090-3325-3107
臼井 充則,090-7661-8985
深沢 ジョージ,080-9273-8284
北野 努,080-8757- 243
久保田 大,080-7019-1834
奥 ヒロ,090-5213-5174
浅見 優一,090-1933-5006
高松 まさし,080-3287-8809
羽田 健,090-3691-4825
根岸 徹,090-5238-9615
三原 光臣,090- 581-8093
長岡 米蔵,080-1606-3401
大和田 完爾,080-4688-9697
堀口 正義,090-8284- 128
福原 惇,080-7191-3891
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Downloads
$
そしてさらに先ほどの実行結果に次のパイプ以降をつなげると
$ cat dummy.csv | awk -F , '$4=="男" && $8=="東京都" { printf("%s,%s\n", $1,$9) }' | nkf -Lw -s >output.csv
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Downloads
$
あっという間にここで抽出条件にあったcsvファイルができてしまいました。
またこの’nkf -Lw -s’というコマンドはCygwinの公式パッケージには含まれているコマンドではありませんが、文字コードをutf-8からShift-JISに変更するためのフィルタプログラムです。
この awk というスクリプト言語はしらかば堂がWindows環境下でもUnix/Linuxの環境を使いたいと考える理由の相当の部分を占めており
前のコマンドの出力結果を’|’(パイプ)を使って次のawkスクリプトの入力として printf 文でコマンド列を作成し、さらに’|’(パイプ)を使ってそれを続くshコマンドに渡してそれを実行する
といったこと、例えばあるフォルダ配下のサブフォルダ配下に含まれるpdfファイルを指定フォルダ配下に一括コピーするといったこと、を本当に簡単に実現可能な最強のスクリプト言語の一つです。興味のある方は、以下の書籍で確認してみてくださいね。
nkf コマンドもCygwinの公式パッケージには含まれていないのですが、nurse 氏を筆頭とする開発者の方が開発された Windows のShift-JISコードと Cygwin のutf-8 コード間の橋渡しをする重要なフィルタ(データストリームを処理するプログラム)です。
興味のある方は@ITの以下の記事
で利用方法等について併せて参照ください。
nkf の最新版を利用する場合には nurse 氏の GitHub に clone されているので v2_1_5 等の tag から tar.gz 拾うか master の tar.gz 拾ってビルドする必要があります。
この記事では入力ファイルをCSVファイル(フィールドを’,’で区切ったファイル)としたためawkコマンドでフィールドの区切りを “-F ,” とし、’,’ をセパレータとして指定していますが、フィールドの文字列の中に’,’を含むケースもあるかと思います。
そういったケースでは入力ファイルをTSVファイル(フィールドをTABで区切ったファイル)にできないかを検討しましょう。文字列のなかに TAB を含むケースではは同様の問題が生じますが、’,’ を含む場合に比べればレアケースでしょう。
この場合はawkコマンドでフィールドの区切りは “-F \t“のように指定します。
データ整形を効率化!Cygwinのawkスクリプトをバッチファイルで実行
Cygwinのawkスクリプトを習得しよう
さて以上を踏まえ、先ほど Cygwin 内で実行したawkコマンドをTeraterm等のエディタで作成し、utf-8コードで保存することで、次の様なスクリプトファイル
cat "$1" | awk -F , '$4=="男" && $8=="東京都" { printf("%s,%s\n", $1,$9) }' | nkf -Lw -s >"${1%.*}_extructed.${1##*.}"
echo "${1%.*}_extructed.${1##*.}" を作成しました。
- get_nameAndPhoneNumber.sh
を作成し
- %USERPROFILE%\Tools\bash
配下に保存し
C:\Users\devel.RVH>cd %USERPROFILE%\Tools\bash
C:\Users\devel.RVH\Tools\bash>dir
ドライブ C のボリューム ラベルがありません。
ボリューム シリアル番号は 2485-30D8 です
C:\Users\devel.RVH\Tools\bash のディレクトリ
2023/08/20 13:28 <DIR> .
2023/08/20 13:28 <DIR> ..
2023/08/20 13:48 85 get_nameAndPhoneNumber.sh
1 個のファイル 85 バイト
2 個のディレクトリ 289,922,293,760 バイトの空き領域
C:\Users\devel.RVH\Tools\bash>
さらにCygwin Terminalでこのファイルにシェルスクリプトとして実行可能とするためにchmod コマンドで x(実行権)パーミッションを追加します。
devel@rvhsv01 ~
$ cd 'C:\Users\devel.RVH\Tools\bash'
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ ls
get_nameAndPhoneNumber.sh
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ cat get_nameAndPhoneNumber.sh
cat "$1" | awk -F , '$4=="男" && $8=="東京都" { printf("%s,%s\n", $1,$9) }' | nkf -Lw -s >"${1%.*}_extructed.${1##*.}"
echo "${1%.*}_extructed.${1##*.}" を作成しました。
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ ls -l
total 1
-rw-rw-rw-+ 1 devel Domain Users 85 Aug 20 13:48 get_nameAndPhoneNumber.sh
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ chmod 775 get_nameAndPhoneNumber.sh
devel@rvhsv01 /cygdrive/c/Users/devel.RVH/Tools/bash
$ ls -l
total 1
-rwxrwxr-x+ 1 devel Domain Users 85 Aug 20 13:48 get_nameAndPhoneNumber.sh
このスクリプト get_nameAndPhoneNumber.sh はその1行目にパイプ’|’、リダイレクション’>’からなる複数のパートから構成されていますが、その内容を紹介すると以下のようになります。
行数 | 項番 | コマンド | 意味 |
---|---|---|---|
1 | 1 | cat “$1” | |
このシェルスクリプトget_nameAndPhoneNumber.shの第1パラメータで指定されるファイル内容をパイプ’|‘経由で次のコマンドの標準入力に渡します。 |
2 | awk -F , ’$4==”男” && $8==”東京都” { printf(“%s,%s\n”, $1,$9) }’ | |
標準入力からのデータを、先ほどCygwin Terminal 内で実行した “,” をフィールドのセパレータとして指定したときの 結果をさらにパイプ’|‘経由で次のコマンドの標準入力に渡します。 |
|
3 | nkf -Lw -s >”${1%.*}_extructed.${1##*.}” |
標準入力からのデータを、nkf コマンドで文字コードをutf-8からShift-JISに変更し、 その結果をリダイレクト’>‘を使って”${1%.*}_extructed.${1##*.}“というファイルに書き込みます。 この”${1%.*}”、”${1##*.}”という表現はbash環境で変数を展開し、ファイル名や拡張子の取得する方法で、 “${1%.*}”は$1の拡張子を取り除いたファイ名 になります。 ここでは空白を含むパスが渡された際にも正しく動作するようにこれらをを””で囲んでいます。 |
|
2 | – | “${1%.*}_extructed.${1##*.}” を作成しました。 | 1行目の項番3で作成したファイル名を確認のために表示しています。 |
ここで1行目の項番1の “$1”はシェルスクリプトget_nameAndPhoneNumber.shのパラメータ順を表していますが
項番2の$1,$9はawkコマンドの標準入力に対するフィールド順番を表しており、意味が違うので注意が必要です。
またbashの変数展開については @mriho さんの素晴らしい記事
がありますので、こちらも必要に応じて参照ください。
Linux/Cygwinのファイルやディレクトリには、アクセス権が設定されます。アクセス権(パーミッション)とは、どのユーザーに対してどういった操作を許可するのかという情報のことです。
アクセス権は、所有者、所有グループに属するユーザー、その他のユーザーの、3種類に対して設定できます。アクセス権には、読み取り可能、書き込み可能、実行可能の3種類があります。
ファイルに対して読み取り権のみが与えられている場合、そのファイルの内容を
読み取ることができますが、変更を加えることはできません。ディレクトリに対して読み取り権が与えられている場合、そのディレクトリ内のファイル一覧を表示すること
ができます。
ファイルに対して書き込み権が与えられている場合、ファイルの内容を変更した
り、削除したりすることができます。ディレクトリに対して書き込み権が与えられている場合、そのディレクトリ内でファイルを作成したり、削除したりすることができます。
ファイルに対して実行権が与えられている場合、そのファイルを実行することができます。ディレクトリに対して実行権が与えられている場合、そのディレクトリ内のファイルにアクセスすることができます。アクセス権は、以下のように表記されます。
権限 | 表記 |
---|---|
読み取り権 | r |
書き込み権 | w |
実行権 | x |
$ ls -l
total 1
-rwxrwxr-x+ 1 devel Domain Users 85 Aug 20 13:48 get_nameAndPhoneNumber.sh
実行結果の左のほうにある「rwxrwxr-x」の部分がアクセス権です。
アクセス権は左から3文字ずつ、「ユーザー(所有者)のアクセス権」「グループのア
クセス権」「その他ユーザーのアクセス権」を表しており、「-」はその部分のアクセス権がないことを意味します。get_nameAndPhoneNumber.shというファイルでは、アクセス権は次のようになっています。
● 所有者に対しては「読み取り可能、書き込み可能、実行可能」
● グループに対しては「読み取り可能、書き込み可能、実行可能」
● その他ユーザーに対しては「読み取り可能、実行可能」
アクセス権の左側の1文字は、ファイルの種別を表します。「-」は通常のファイ
ル、「d」はディレクトリ、「l」はリンクを表します。
またアクセス権は、数値でも表すことができます。この場合、「読み取り=4」「書き込み=2」「実行=1」として、所有者、グループ、その他ユーザーごとに足した数
値で表します。
所有者 | グループ | その他ユーザー | |
---|---|---|---|
rwx | rwx | r-x | :記号表記 |
421 | 421 | 41 | |
↓ | ↓ | ↓ | |
7 | 7 | 5 | :数字表記 |
バッチファイルの作成手順をステップバイステップで解説
そしてこのスクリプトファイルを起動するバッチファイルとして次のバッチファイル
- GetNameAndPhoneNumber.bat
を作成し
- %USERPROFILE%\Tools\bat
配下に保存しましす。
@echo on
setlocal
PATH=C:\cygwin64\bin;%PATH%
bash --login -i -c """$(cygpath -u ""C:\Users\devel.RVH\Tools\bash\get_nameAndPhoneNumber.sh"")"" '%~1'"
endlocal
こちらのバッチファイルについても簡単にその内容を説明します。
行 | 説明 |
---|---|
1 | このコマンド以降以降の出力を抑制します。 |
2 | バッチファイル中のendlocalまでに利用する変数(具体的には$PATH)をローカル変数として利用する宣言をします |
3 | このバッチファイルの中でCygwinのコマンドが利用可能なようにWindowsの実行パス%PATH%にCygwinの実行パス”C:\cygwin64\bin“を追加します。 |
4 |
バッチファイルからCygwin環境下のbash経由でシェルスクリプト |
5 |
3行目で宣言したローカル変数宣言を解除します。 |
(注) バッチファイルから Cygwin の bash を実行する際は次のように記述します。
\cygwin64\bin\bash –login -i -c “bash に渡すコマンド文字列”
この実行時パラメータの意味は以下のようになっています。
パラメータ | 意味 |
---|---|
-login | Cygwin へログインする |
-i | bash をインタラクティブモード、つまり後続のコマンド文字列が端末から入力されたかのようにbash を動かすことを指定 |
-c | 次にくる2つのダブルクオーテーション ” ” で囲んだ文字列を bash に渡すコマンド文字列とすること |
バッチファイルのパラメータに関する修飾演算子に付いては必要に応じて下記の関連記事を参照ください。
バッチファイル実行の結果を詳細に分析
以下がこの
%USERPROFILE%\Downloads\dummy.csv
に対し
- GetNameAndPhoneNumber.bat
を実行した結果です。
Microsoft Windows [Version 10.0.19045.3324]
(c) Microsoft Corporation. All rights reserved.
C:\Users\devel.RVH>cd %USERPROFILE%\Tools\bat
C:\Users\devel.RVH\Tools\bat>dir GetN*
ドライブ C のボリューム ラベルがありません。
ボリューム シリアル番号は 2485-30D8 です
C:\Users\devel.RVH\Tools\bat のディレクトリ
2023/08/21 05:29 583 GetNameAndPhoneNumber.bat
1 個のファイル 583 バイト
0 個のディレクトリ 289,975,209,984 バイトの空き領域
C:\Users\devel.RVH\Tools\bat>GetNameAndPhoneNumber %USERPROFILE%\Downloads\dummy.csv
\Users\devel.RVH\Downloads\dummy_extructed.csv を作成しました。
C:\Users\devel.RVH\Tools\bat>
そしてこれが抽出結果
%USERPROFILE%\Downloads\dummy_extructed.csv
です。
ほんの一瞬で変換が終わりました。
まとめ:データ整形の驚くべき方法がここに
いかがですか?
この記事では、バッチファイルとCygwinのawkスクリプトを組み合わせ、数行のコードで、以下の例に示す複雑なデータ整形をPowerQueryを使わずに手軽に実現可能とする驚くべき方法を紹介しました。
この記事ではCygwinの基本から始め、awkスクリプトの作り方、バッチファイルと Cygwinの awk スクリプトを組み合わせ方まで順にご紹介しました。
この方法はawkスクリプト部分をカスタマイズすることで、こういったデータ整形のみならずWindowsのファイルの一括移動、名称変更、一括削除等にも流用が可能な汎用的な技術ですので是非あなたの業務効率化にお役立て下さい。
この記事が少しでも読者の方のお役に立てれば幸いです。
この記事には @ac4tweet さんより沢山の貴重なアドバイスを頂いています。
@ac4tweet さんには心より御礼申し上げます。