フィールド名に使えない文字をなんとかする

[monitor csv file from hwinfo@Splunk>Answer](https://answers.splunk.com/answers/799331/monitor-csv-file-from-hwinfo.html#answer-798637)に回答した時の手順や考え方の忘備録

ただし

```config:props.conf
HEADER_FIELD_ACCEPTABLE_SPECIAL_CHARACTERS = %
TIMESTAMP_FIELDS = Date,Time
TIME_FORMAT = %d.%m.%Y%T.%3Q
```
で解決したそうです。:sweat:

#データ
```java:hwinfo.spl
| makeresults 
 | eval _raw="Date,Time,\"CPU (Tctl/Tdie) [°C]\",\"CPU Die (average) [°C]\",\"CPU CCD1 (Tdie) [°C]\",\"CPU CCD2 (Tdie) [°C]\",\"CPU PPT Limit [%]\",\"CPU TDC Limit [%]\",\"CPU EDC Limit [%]\",\"Chipset [°C]\",\"System1 [°C]\",\"CPU [°C]\",\"PCIEX16_1 [°C]\",\"VRM MOS [°C]\",\"Chipset [°C]\",\"CPU [RPM]\",\"System 1 [RPM]\",\"System 2 [RPM]\",\"System 3/PCH [RPM]\",\"PCIEX16_2 [°C]\",\"System2 [°C]\",\"System 5 Pump [RPM]\",\"System 6 Pump [RPM]\",\"System 4 [RPM]\",\"VR Loop1 [°C]\",\"GPU Temperature [°C]\",\"GPU Fan1 [RPM]\",\"GPU Fan2 [RPM]\",\"GPU Core Load [%]\",\"GPU Memory Controller Load [%]\",\"GPU Video Engine Load [%]\",\"GPU Bus Load [%]\",\"GPU Memory Usage [%]\",\"GPU D3D Usage [%]\",\"GPU Video Decode 0 Usage [%]\",\"GPU Video Encode 0 Usage [%]\",\"GPU Computing (Compute_0) Usage [%]\",\"GPU Computing (Compute_1) Usage [%]\",\"GPU VR Usage [%]\",
  31.1.2020,13:5:58.138,38.1,36.3,36.0,33.3,25.8,9.6,19.7,65.4,35,38,46,42,52,1099,636,600,1757,42,40,2896,535,559,43.0,37,0,0,7.0,7.0,0.0,0.0,18.7,5.6,0.0,0.0,0.0,0.0,0.0,
  31.1.2020,13:6:0.213,36.6,36.6,36.5,33.0,26.3,10.3,21.7,65.4,35,36,46,42,52,1101,639,600,1753,42,40,2922,535,558,43.0,37,0,0,10.0,8.0,0.0,1.0,18.7,9.2,0.0,0.0,0.0,0.0,0.0,
  31.1.2020,13:6:2.286,36.8,36.9,36.3,33.0,26.2,10.1,23.3,65.4,35,36,46,42,52,1101,635,600,1753,42,40,2836,535,558,43.0,37,0,0,7.0,7.0,0.0,0.0,18.7,5.9,0.0,0.0,0.0,0.0,0.0," 
```
よくある、コマンドの出力結果をそのままSplunkに取り込んだもの

#自動で抽出した場合
```java:auto.spl
....
| multikv forceheader=1
| fields - _time _raw linecount
```
####結果

|CPU_CCD1__Tdie_____C__|	CPU_CCD2__Tdie_____C__|	CPU_Die__average_____C__|...|
|:--|:--|:--|:--|
|36.0|33.3|36.3|...|
なんというか、ひどい。

[Configureindex-timefieldextraction](https://docs.splunk.com/Documentation/Splunk/8.0.1/Data/Configureindex-timefieldextraction)によると

- Field name syntax restrictions
You can assign field names as follows:

 - Valid characters for field names are a-z, A-Z, 0-9, or _ .
 - Field names cannot begin with 0-9 or _ . Splunk reserves leading -underscores for its internal variables.
 - Avoid assigning field names that match any of the default field names.
 - Do not assign field names that contain international characters.

ということなので、`(`や`[`、` `とかが`_`に変換されてしまっている。
#フィールド名について
Splunkだと行を順番に並べるのは`sort`や`streamstats count`とかで順番を保持できる。
ただ列を順番に並べるとなるとこれまた、大変。
例えば `foreach *`の`*`の順番って、実は文字列順。
`table A C B`だとすると　`A` `B` `C`の順で処理される。
となると　`table 1_A 2_C 10_B` だと　`1_A` `10_B` `2_C`の順になる。
この数字が文字列順になるところがSplunkの列名の厳しいところ。
じゃ`table`に引数が使えるかというと、**使えない**
`table $table_order$`が使えたなら、この記事を書いていないと思います。
なお、ダッシュボードだと使えます。テーブル名を自動的に揃えたいならダッシュボードにすると楽です。

[sort](https://docs.splunk.com/Documentation/Splunk/latest/SearchReference/Sort)のところに根拠

- Lexicographical order
 - Lexicographical order sorts items based on the values used to encode the items in computer memory. In Splunk software, this is almost always UTF-8 encoding, which is a superset of ASCII.

 - Numbers are sorted before letters. Numbers are sorted based on the first digit. For example, the numbers 10, 9, 70, 100 are sorted lexicographically as 10, 100, 70, 9.
 - Uppercase letters are sorted before lowercase letters.
 - Symbols are not standard. Some symbols are sorted before numeric values. Other symbols are sorted before or after letters.


#列名の順番保持
`transpose 0`で一旦縦にして、頭に`ABCDEFGHI...`を順番につけてあげるといいです。
いろいろやったあと、`|transpose 0 header_field=column`で戻してあげる。

```java:rename.spl
| makeresults 
| fillnull a_TEST b_TEST2 c_TEST3
| foreach *_* [ rename <<FIELD>> as <<MATCHSEG2>>]
```
こんな感じで一括`rename`できますし。
でも、今回は列名に記号が入っていて、`foreach`を通すと自動的に列名が変更されてしまうので使えなかった。

#最終SPL
```java:final_answer.spl
....
| makemv delim="
  " _raw
 | eval header=mvindex(_raw,0)
 | eval raw=mvindex(_raw,1,-1)
 | stats list(header) as header by raw
 | eval tmp=mvzip(split(header,","),split(raw,","),"#")
 | streamstats count as session
 | stats count by tmp session
 | eval fieldname=mvindex(split(tmp,"#"),0) , value=mvindex(split(tmp,"#"),1)
 | eval {fieldname} = value
 | fields - tmp count fieldname value
 | stats values(*) as * by session
 | rename \"*\" as *
 | fields - session
 | table Date,Time,"CPU (Tctl/Tdie) [°C]","CPU Die (average) [°C]","CPU CCD1 (Tdie) [°C]","CPU CCD2 (Tdie) [°C]","CPU PPT Limit [%]","CPU TDC Limit [%]","CPU EDC Limit [%]","Chipset [°C]","System1 [°C]","CPU [°C]","PCIEX16_1 [°C]","VRM MOS [°C]","Chipset [°C]","CPU [RPM]","System 1 [RPM]","System 2 [RPM]","System 3/PCH [RPM]","PCIEX16_2 [°C]","System2 [°C]","System 5 Pump [RPM]","System 6 Pump [RPM]","System 4 [RPM]","VR Loop1 [°C]","GPU Temperature [°C]","GPU Fan1 [RPM]","GPU Fan2 [RPM]","GPU Core Load [%]","GPU Memory Controller Load [%]","GPU Video Engine Load [%]","GPU Bus Load [%]","GPU Memory Usage [%]","GPU D3D Usage [%]","GPU Video Decode 0 Usage [%]","GPU Video Encode 0 Usage [%]","GPU Computing (Compute_0) Usage [%]","GPU Computing (Compute_1) Usage [%]","GPU VR Usage [%]",
```
最後の`table`の列名は元のデータからのコピペ。
これを_props.conf_や_transforms.conf_で設定するの大変だと思う。

#解説
1. もとのデータをmultivalueに変換。このごろはmultivalueでいろいろすることが多い。`mvindex`が万能
2. ヘッダー部分を抽出
3. データ部分を抽出
4. ヘッダーの順番を保持(`stats list()`)しながらデータ部分ごとに、ヘッダーとデータの行を作成
5. ヘッダーとデータを一対一とした一時的multivalueの作成
6. 行の順番保持と一意の値にするため`streamstats`でセッション番号を作成。これは本当によく使います。
7. ヘッダーごとに行を作成。`mvexpand`を使うやり方が逆にわからなくなってきた。
8. フィールド名と値をフィールドとして分解
9. フィールドに値を導入。 `| eval {fieldname} = value`<br>これだと列名が変換されない。
10. 不要なフィールドを削除
11. 先に作ったセッション番号を利用して、各列を行としてまとめる
12. 余計な`"`を削除<br>今回 `| rename \"*\" as *`で`"`にエスケープが必要なことを初めて知りました。
13. セッション番号を削除。お疲れ様でした。
14. コピペした順番で整列

[元記事](https://answers.splunk.com/answers/799331/monitor-csv-file-from-hwinfo.html#answer-798637)にはこれの前のSPLも載っているので、違いをみてもらうとわかりやすいかもしれません。

#まとめ
`| eval {fieldname} = value`を使うために逆算してSPLを組んでいきました。
Windowsやlinuxのコマンドの結果ってSplunkのフィールド名としてそのまま使うのは厳しい時があるので、頑張って_transforms.conf_を書く必要があります。
(試していないけど、`HEADER_FIELD_ACCEPTABLE_SPECIAL_CHARACTERS`なるものが_props.conf_にある。この設定でいけるかも)⇨<a color="red">いけました。</a>
めんどい場合はこのようにSPL作ってコピペ＆サーチですね。

:sweat: CSV 2000行くらいなら、コピペして`multikv`でいいことを最近知りました。
データ取り込む必要なし。