質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

Q&A

解決済

3回答

5462閲覧

windowsのコマンドラインでcsv(tsv)ファイルのソート

kokopachi

総合スコア2

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

0グッド

0クリップ

投稿2020/06/01 13:22

windows10のコマンドラインでcsv(tsv)ファイル(1行目は項目名)を複数条件でソート出来るソフトもしくは方法を教えて下さい。

<動作ダメ>
hsort.exe /c /d\t /p\n /k1a /k2a /otemp.tsv temp.tsv

vectorでhsortが同様のフリーソフトが有るのですが、1行目の特別処理が無いのでhspでファイルを読み込んで2行目以降を出力してそれを処理して取り込む方法で代用しています。
hsortは何かバグが有るらしく、あるファイル(条件は不明)に対してはソート出力をしないので困っています。

<正常OK>
hsort.exe /c /d\t /p\n /k1a /k2a temp.tsv >temp.tsv

ファイル出力(/o)のオプションでは出力しないのですが、標準出力では出力されるのでcsv(tsv)ファイルが壊れている訳ではないのです。

powershell -c "$a=gc temp.tsv; $($a | select -f 1; $a | select -skip 1 | sort{($_ -split '\t')[0]} ) | sc temp.tsv"

パワーシェルでは複数条件の設定方法が私のスキルでは全く分かりません。

複数ファイルでそれぞの項目について数値で↑順、文字列で↓順とそれぞれソート条件が異なります。

どなたかご教授お願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

パワーシェルでは複数条件の設定方法が私のスキルでは全く分かりません。

に関しては、以下のページでSort-Objectコマンドレットを使用する場合の例が載っています。

オブジェクトの並べ替え - PowerShell | Microsoft Docs

質問の状況に照らし合わせると、以下のような使い方のイメージです。

powershell

1# 確認用のため、メモリ上の文字列でデータを用意している。 2[string]$tsvText = @" 3文字列として扱いたい列`t数値として扱いたい列 4AAA`t 9 5BBB`t 2 6BBB`t 1 7AAA`t 10 8"@ 9 10# $tsvText をタブ区切り文字列としてパースしてオブジェクト化(`tはタブ文字)。 11$tsvText | ConvertFrom-Csv -Delimiter "`t" | 12<# ファイルから読み込むなら以下のような方法がある。 13Get-Content -LiteralPath .\temp.tsv | ConvertFrom-Csv -Delimiter "`t" | 14Import-Csv -LiteralPath .\temp.tsv -Delimiter "`t" -Encoding Default | 15#> 16Sort-Object -Property @( 17 # Expression に列名または、スクリプトブロック({})を指定。Ascending|Descending で昇順・降順指定。 18 @{Expression = '文字列として扱いたい列' ; Descending = $true}, 19 # デフォルトでは文字列扱いなので数値に型変換 20 @{Expression = {[decimal]$_.'数値として扱いたい列'}; Ascending = $true} 21) | 22# 文字列化。 23ConvertTo-Csv -Delimiter "`t" -NoTypeInformation 24# ファイルへ出力するなら Export-Csv -LiteralPath .\temp.tsv なり Set-Content なりで。

上記コマンドの結果は以下になります。

text

1"文字列として扱いたい列" "数値として扱いたい列" 2"BBB" "1" 3"BBB" "2" 4"AAA" "9" 5"AAA" "10"

結果を見るとわかるかと思いますが、それぞれの値が"で囲まれるようになります。
これは PowerShell の~-Csv系コマンドレットの仕様のため、避けたい場合は~-Csv系のコマンドレットを使用せず自前で分割するか、最新の PowerShell 7(pwsh.exe) をインストールしてください。


ちなみに、Sort-ObjectPropertyパラメータに渡すハッシュテーブルのキーのExpressionAscendingDescendingは、最短1文字まで縮めて指定しても良いです。

このことを踏まえて、上記のコードを圧縮すると以下のようになります。

powershell

1[string]$tsvText = @" 2文字列として扱いたい列`t数値として扱いたい列 3AAA`t 9 4BBB`t 2 5BBB`t 1 6AAA`t 10 7"@ 8 9$tsvText | ConvertFrom-Csv -D "`t" | 10sort @{E='文字列として扱いたい列';D=$true}, @{E={[decimal]$_.'数値として扱いたい列'};A=$true} | 11ConvertTo-Csv -D "`t" -N

投稿2020/06/02 11:15

imihito

総合スコア2166

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kokopachi

2020/06/03 01:12

temp.tsv test.bat busybox64.exe -------------------------- <test.bat>の内容 ----------------------------- busybox64.exe bash -c "head -n1 temp.tsv && tail -n +2 temp.tsv | sort -k2,2 -k1,1 -k3,3" >sort.tsv ---------------------------- 早速の回答ありがとうございます。 batファイルで以上の内容で動作しましたが、まだ不都合が有りました。 バイナリエディタなどでファイルの内容を直接見てみた所・・・ >sort.csv ファイルの末尾が\nで出力されていないらしく、本来は0D0Aで終わっているのが0Aで終わっていました。 csvエディタなどで正常に読み込めません。 いったんテキストエディタで読んでセーブしなおすと治ります。 これはコマンドラインの限界なのでしょうか?
imihito

2020/06/03 10:25

KojiDoi さんの回答へのコメントと思われますので、そちらに再度コメントしてください(KojiDoi さんへ通知がいかないため)。
guest

0

ベストアンサー

busyboxを導入すると便利だと思います。

busyboxはlinuxなどのlinux-like OSではおなじみのbash, head, tail, sortなどのコマンドとして機能する十徳ナイフ的ツールです。

busyboxをbashとして起動します。

tsvファイルの一行目はそのまま、2行目以降をソートして新たなファイルとするには次のようなワンライナーが使えます。

(head -n 1 input.tsv && tail -n +2 input.tsv|sort -t$'\t') > output.tsv

(↑ 2020/06/03修正。headのパラメータ書式がlinuxとbusyboxでは違っていた…)

sortの段階で複数キーを使うことも自由に出来ます。例えば、第4カラムを数値キーとしてソートし、さらに第1カラムを文字列としてソートしたいならこんな感じで。

(head -n +1 input.tsv && tail -n +1 input.tsv|sort -t$'\t' -k 4n,4 -k 1) > output.tsv

実例

busybox64.exeが既にpathの引かれたディレクトリに置かれていて、CMDのプロンプトから呼び出せる状況になっているとします。これだけでDOSプロンプト下でbashをはじめ、head, tail, awk, grep, sed, perl, sort ...などのツールが使えるようになります。

私の環境ではd:\mydocumentの下にいろいろtsvファイルが置いてあるので、これを試しにソートしてみることにします。まず普通にDOS窓からファイル内容を出力してみます。

D:\mydocument>type test.tsv ID Value AA 1 ZZ 2 BB 20 CC 2 AA 2 AB 3

busybox経由でとりあえずソートしてみます。1行目はそのまま、2行目以降を普通に文字列としてソートします。

D:\mydocument>busybox64 bash -c "head -n 1 test.tsv && tail -n +2 test.tsv | sort" ID Value AA 1 AA 2 AB 3 BB 20 CC 2 ZZ 2

条件を少し複雑化して、2列目を数値キーとしてソートし、さらに1列目でソートしてみます。

D:\mydocument>busybox64 bash -c "head -n 1 test.tsv && tail -n +2 test.tsv | sort -k 2n,2 -k 1,1" ID Value AA 1 AA 2 CC 2 ZZ 2 AB 3 BB 20

毎回これらのコマンドを打ち込むのは面倒なのでシェルスクリプトにしてみます。
次のような内容のファイルを作成してsorttest.shという名前で保存します。

#!/bin/sh infile=$1 outfile=${infile}.out (head -n 1 ${infile} && tail -n +2 ${infile} | sort -k 2n,2 -k 1,1) > ${outfile}

とりあえず、コマンドラインから対象ファイル名を読み取り、その後ろに.outをつけた名前のファイルにソート結果をセーブするという仕様としてあります。

実行してみます。

D:\mydocument>busybox64 bash sorttest.sh test.tsv D:\mydocument>type test.tsv.out ID Value AA 1 AA 2 CC 2 ZZ 2 AB 3 BB 20

改行コードの違いを解決する(20-06-03追記)

busyboxが想定している改行コードとwindowsのそれが違っていることを忘れていました。

  • busybox: LF
  • windows: CR+LF

最後に各行末をチェックし、これをCR+LFに揃える措置が必要のようです。
スクリプトファイルの最終行を次のようにすると解決するはず。

(head -n 1 ${infile} && tail -n +2 ${infile} | sort -k 2n,2 -k 1,1) | sed 's/\r$//; s/$/\r/' > ${outfile}

おわりに

というわけで、busyboxの存在はもっと知られてもいいのではないかと思う次第です。より本格的にやりたければMSYS2とかWindows Subsystem for Linuxを考えるべきですが、ファイルをたった一個ダウンロードしてくるだけで準備完了という手軽さは、他の追随を許さない美点と言えます。

投稿2020/06/01 15:54

編集2020/06/03 11:40
KojiDoi

総合スコア13669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kokopachi

2020/06/02 03:27

早速の回答ありがとうございます。 早速「bustbox64.exe」をダウンロードして試してみましたがうまくいきません。 <動作不可> busybox64.exe (head -n 1 temp.tsv && tail -n +1 temp.tsv|sort -t$'\t') >temp.tsv >busybox64.exe head -n +1 temp.tsv >>head: invalid number '+1' >busybox64.exe sort -t$'\t' temp.tsv >sort.tsv >>sort: bad -t parameter 細かく区切って色々と試行してみた所<head -n +1>の<+1>の指定がダメみたいです。 sortの区切り文字の<sort -t$'\t'>の<-t$'\t'>もダメみたいです。
KojiDoi

2020/06/02 05:07

(head -n 1 temp.tsv && tail -n +1 temp.tsv|sort -t$'\t') >temp.tsv の部分はbashのスクリプトです。この書き方だとbashに処理が渡される前にcmdが解釈しようとしてしまうのでうまくいきません。シェルスクリプトファイル(bash版バッチファイルのようなもの)を作ってbusyboxに渡す必要があると思います。今晩余裕があったら追記します。
kokopachi

2020/06/04 12:06

素晴らしい回答ありがとうございます。 これで長年のモヤモヤが解決しました。 回答が複雑になってしまったのでまとめてみました。 Q. windows10のコマンドラインで1行目は項目名の内容のtsvファイル(tabで区切られたcsvファイル)を複数条件でソートする方法。 【準備1】 http://frippery.org/busybox/ このページから「busybox64.exe」という文字列を検索してクリックしてダウンロードする。 【準備2】 ---sort.txt--- #!/bin/sh (head -n1 $2 && tail -n+2 $2 | sort -t$'\t' $1 ) | sed 's/\r$//; s/$/\r/' >$3 -------------- この内容でsort.txtをセーブ。 【書式】 busybox64.exe bash sort.txt "ソートオプション" "入力ファイル" "出力ファイル" 【書式サンプル】 busybox64.exe bash sort.txt "-k1 -k2 -k3" "temp.tsv" "temp_out.tsv" 【ソートオプション】 https://hydrocul.github.io/wiki/commands/sort.html ソートオプションの詳細はここで調べます。
guest

0

Q.
windows10のコマンドラインで1行目は項目名の内容のtsvファイル(tabで区切られたcsvファイル)を複数条件でソートする方法。

【準備1】
http://frippery.org/busybox/
このページから「busybox64.exe」という文字列を検索してクリックしてダウンロードする。

【準備2】
---sort.txt---
#!/bin/sh
(head -n1 $2 && tail -n+2 $2 | sort -t$'\t' $1 ) | sed 's/\r$//; s/$/\r/' >$3

この内容でsort.txtをセーブ。

【書式】
busybox64.exe bash sort.txt "ソートオプション" "入力ファイル" "出力ファイル"

【書式サンプル】
busybox64.exe bash sort.txt "-k1,1 -k2,2 -k3,3" "temp.tsv" "temp_out.tsv"

【注意点】
最初の列は1列目。0では無い。
5列目を指定する時は-k5,5と記述。

【ソートオプション】
https://hydrocul.github.io/wiki/commands/sort.html
ソートオプションの詳細はここで調べます。

sortコマンドでCSVファイルをソートする場合はソート列の指定方法に注意 | ゲンゾウ用ポストイット
https://genzouw.com/entry/2019/04/24/083709/1399/

投稿2020/06/04 12:12

編集2020/06/04 12:58
kokopachi

総合スコア2

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問