mirror of
https://github.com/johnkerl/miller.git
synced 2026-01-23 02:14:13 +00:00
Implement all/by-regex field selection (-a/-r) for mlr sub, gsub, and ssub (#1480)
* Code-dedupe `sub`, `gsub`, and `ssub` verbs * More dedupe * Start with -a * Implement -r * unit-test cases * Windows command-line parsing
This commit is contained in:
parent
81d11365a0
commit
e5ec9f67bd
42 changed files with 475 additions and 518 deletions
|
|
@ -1,157 +0,0 @@
|
|||
package transformers
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/johnkerl/miller/pkg/bifs"
|
||||
"github.com/johnkerl/miller/pkg/cli"
|
||||
"github.com/johnkerl/miller/pkg/mlrval"
|
||||
"github.com/johnkerl/miller/pkg/types"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
const verbNameGsub = "gsub"
|
||||
|
||||
var GsubSetup = TransformerSetup{
|
||||
Verb: verbNameGsub,
|
||||
UsageFunc: transformerGsubUsage,
|
||||
ParseCLIFunc: transformerGsubParseCLI,
|
||||
IgnoresInput: false,
|
||||
}
|
||||
|
||||
func transformerGsubUsage(
|
||||
o *os.File,
|
||||
) {
|
||||
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameGsub)
|
||||
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
|
||||
fmt.Fprintf(o, "for the old string and handling multiple matches, like the `gsub` DSL function.\n")
|
||||
fmt.Fprintf(o, "See also the `sub` and `ssub` verbs.\n")
|
||||
fmt.Fprintf(o, "Options:\n")
|
||||
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
|
||||
fmt.Fprintf(o, "-h|--help Show this message.\n")
|
||||
}
|
||||
|
||||
func transformerGsubParseCLI(
|
||||
pargi *int,
|
||||
argc int,
|
||||
args []string,
|
||||
_ *cli.TOptions,
|
||||
doConstruct bool, // false for first pass of CLI-parse, true for second pass
|
||||
) IRecordTransformer {
|
||||
|
||||
// Skip the verb name from the current spot in the mlr command line
|
||||
argi := *pargi
|
||||
verb := args[argi]
|
||||
argi++
|
||||
|
||||
// Parse local flags
|
||||
var fieldNames []string = nil
|
||||
var oldText string
|
||||
var newText string
|
||||
|
||||
for argi < argc /* variable increment: 1 or 2 depending on flag */ {
|
||||
opt := args[argi]
|
||||
if !strings.HasPrefix(opt, "-") {
|
||||
break // No more flag options to process
|
||||
}
|
||||
if args[argi] == "--" {
|
||||
break // All transformers must do this so main-flags can follow verb-flags
|
||||
}
|
||||
argi++
|
||||
|
||||
if opt == "-h" || opt == "--help" {
|
||||
transformerGsubUsage(os.Stdout)
|
||||
os.Exit(0)
|
||||
|
||||
} else if opt == "-f" {
|
||||
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
|
||||
} else {
|
||||
transformerGsubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if fieldNames == nil {
|
||||
transformerGsubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the old and new text from the command line
|
||||
if (argc - argi) < 2 {
|
||||
transformerGsubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
oldText = args[argi]
|
||||
newText = args[argi+1]
|
||||
|
||||
argi += 2
|
||||
|
||||
*pargi = argi
|
||||
if !doConstruct { // All transformers must do this for main command-line parsing
|
||||
return nil
|
||||
}
|
||||
|
||||
transformer, err := NewTransformerGsub(
|
||||
fieldNames,
|
||||
oldText,
|
||||
newText,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return transformer
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
type TransformerGsub struct {
|
||||
fieldNames []string
|
||||
oldText *mlrval.Mlrval
|
||||
newText *mlrval.Mlrval
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
func NewTransformerGsub(
|
||||
fieldNames []string,
|
||||
oldText string,
|
||||
newText string,
|
||||
) (*TransformerGsub, error) {
|
||||
tr := &TransformerGsub{
|
||||
fieldNames: fieldNames,
|
||||
oldText: mlrval.FromString(oldText),
|
||||
newText: mlrval.FromString(newText),
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (tr *TransformerGsub) Transform(
|
||||
inrecAndContext *types.RecordAndContext,
|
||||
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
|
||||
inputDownstreamDoneChannel <-chan bool,
|
||||
outputDownstreamDoneChannel chan<- bool,
|
||||
) {
|
||||
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
|
||||
|
||||
if !inrecAndContext.EndOfStream {
|
||||
inrec := inrecAndContext.Record
|
||||
|
||||
for _, fieldName := range tr.fieldNames {
|
||||
oldValue := inrec.Get(fieldName)
|
||||
if oldValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newValue := bifs.BIF_gsub(oldValue, tr.oldText, tr.newText)
|
||||
|
||||
inrec.PutReference(fieldName, newValue)
|
||||
}
|
||||
|
||||
outputRecordsAndContexts.PushBack(inrecAndContext)
|
||||
} else {
|
||||
outputRecordsAndContexts.PushBack(inrecAndContext) // emit end-of-stream marker
|
||||
}
|
||||
}
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
package transformers
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/johnkerl/miller/pkg/bifs"
|
||||
"github.com/johnkerl/miller/pkg/cli"
|
||||
"github.com/johnkerl/miller/pkg/mlrval"
|
||||
"github.com/johnkerl/miller/pkg/types"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
const verbNameSsub = "ssub"
|
||||
|
||||
var SsubSetup = TransformerSetup{
|
||||
Verb: verbNameSsub,
|
||||
UsageFunc: transformerSsubUsage,
|
||||
ParseCLIFunc: transformerSsubParseCLI,
|
||||
IgnoresInput: false,
|
||||
}
|
||||
|
||||
func transformerSsubUsage(
|
||||
o *os.File,
|
||||
) {
|
||||
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSsub)
|
||||
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), without regex support for\n")
|
||||
fmt.Fprintf(o, "the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.\n")
|
||||
fmt.Fprintf(o, "Options:\n")
|
||||
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
|
||||
fmt.Fprintf(o, "-h|--help Show this message.\n")
|
||||
}
|
||||
|
||||
func transformerSsubParseCLI(
|
||||
pargi *int,
|
||||
argc int,
|
||||
args []string,
|
||||
_ *cli.TOptions,
|
||||
doConstruct bool, // false for first pass of CLI-parse, true for second pass
|
||||
) IRecordTransformer {
|
||||
|
||||
// Skip the verb name from the current spot in the mlr command line
|
||||
argi := *pargi
|
||||
verb := args[argi]
|
||||
argi++
|
||||
|
||||
// Parse local flags
|
||||
var fieldNames []string = nil
|
||||
var oldText string
|
||||
var newText string
|
||||
|
||||
for argi < argc /* variable increment: 1 or 2 depending on flag */ {
|
||||
opt := args[argi]
|
||||
if !strings.HasPrefix(opt, "-") {
|
||||
break // No more flag options to process
|
||||
}
|
||||
if args[argi] == "--" {
|
||||
break // All transformers must do this so main-flags can follow verb-flags
|
||||
}
|
||||
argi++
|
||||
|
||||
if opt == "-h" || opt == "--help" {
|
||||
transformerSsubUsage(os.Stdout)
|
||||
os.Exit(0)
|
||||
|
||||
} else if opt == "-f" {
|
||||
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
|
||||
} else {
|
||||
transformerSsubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if fieldNames == nil {
|
||||
transformerSsubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the old and new text from the command line
|
||||
if (argc - argi) < 2 {
|
||||
transformerSsubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
oldText = args[argi]
|
||||
newText = args[argi+1]
|
||||
|
||||
argi += 2
|
||||
|
||||
*pargi = argi
|
||||
if !doConstruct { // All transformers must do this for main command-line parsing
|
||||
return nil
|
||||
}
|
||||
|
||||
transformer, err := NewTransformerSsub(
|
||||
fieldNames,
|
||||
oldText,
|
||||
newText,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return transformer
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
type TransformerSsub struct {
|
||||
fieldNames []string
|
||||
oldText *mlrval.Mlrval
|
||||
newText *mlrval.Mlrval
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
func NewTransformerSsub(
|
||||
fieldNames []string,
|
||||
oldText string,
|
||||
newText string,
|
||||
) (*TransformerSsub, error) {
|
||||
tr := &TransformerSsub{
|
||||
fieldNames: fieldNames,
|
||||
oldText: mlrval.FromString(oldText),
|
||||
newText: mlrval.FromString(newText),
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (tr *TransformerSsub) Transform(
|
||||
inrecAndContext *types.RecordAndContext,
|
||||
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
|
||||
inputDownstreamDoneChannel <-chan bool,
|
||||
outputDownstreamDoneChannel chan<- bool,
|
||||
) {
|
||||
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
|
||||
|
||||
if !inrecAndContext.EndOfStream {
|
||||
inrec := inrecAndContext.Record
|
||||
|
||||
for _, fieldName := range tr.fieldNames {
|
||||
oldValue := inrec.Get(fieldName)
|
||||
if oldValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newValue := bifs.BIF_ssub(oldValue, tr.oldText, tr.newText)
|
||||
|
||||
inrec.PutReference(fieldName, newValue)
|
||||
}
|
||||
|
||||
outputRecordsAndContexts.PushBack(inrecAndContext)
|
||||
} else {
|
||||
outputRecordsAndContexts.PushBack(inrecAndContext) // emit end-of-stream marker
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
package transformers
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/johnkerl/miller/pkg/bifs"
|
||||
"github.com/johnkerl/miller/pkg/cli"
|
||||
"github.com/johnkerl/miller/pkg/mlrval"
|
||||
"github.com/johnkerl/miller/pkg/types"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
const verbNameSub = "sub"
|
||||
|
||||
var SubSetup = TransformerSetup{
|
||||
Verb: verbNameSub,
|
||||
UsageFunc: transformerSubUsage,
|
||||
ParseCLIFunc: transformerSubParseCLI,
|
||||
IgnoresInput: false,
|
||||
}
|
||||
|
||||
func transformerSubUsage(
|
||||
o *os.File,
|
||||
) {
|
||||
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSub)
|
||||
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
|
||||
fmt.Fprintf(o, "for the old string and not handling multiple matches, like the `sub` DSL function.\n")
|
||||
fmt.Fprintf(o, "See also the `gsub` and `ssub` verbs.\n")
|
||||
fmt.Fprintf(o, "Options:\n")
|
||||
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
|
||||
fmt.Fprintf(o, "-h|--help Show this message.\n")
|
||||
}
|
||||
|
||||
func transformerSubParseCLI(
|
||||
pargi *int,
|
||||
argc int,
|
||||
args []string,
|
||||
_ *cli.TOptions,
|
||||
doConstruct bool, // false for first pass of CLI-parse, true for second pass
|
||||
) IRecordTransformer {
|
||||
|
||||
// Skip the verb name from the current spot in the mlr command line
|
||||
argi := *pargi
|
||||
verb := args[argi]
|
||||
argi++
|
||||
|
||||
// Parse local flags
|
||||
var fieldNames []string = nil
|
||||
var oldText string
|
||||
var newText string
|
||||
|
||||
for argi < argc /* variable increment: 1 or 2 depending on flag */ {
|
||||
opt := args[argi]
|
||||
if !strings.HasPrefix(opt, "-") {
|
||||
break // No more flag options to process
|
||||
}
|
||||
if args[argi] == "--" {
|
||||
break // All transformers must do this so main-flags can follow verb-flags
|
||||
}
|
||||
argi++
|
||||
|
||||
if opt == "-h" || opt == "--help" {
|
||||
transformerSubUsage(os.Stdout)
|
||||
os.Exit(0)
|
||||
|
||||
} else if opt == "-f" {
|
||||
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
|
||||
} else {
|
||||
transformerSubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if fieldNames == nil {
|
||||
transformerSubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the old and new text from the command line
|
||||
if (argc - argi) < 2 {
|
||||
transformerSubUsage(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
oldText = args[argi]
|
||||
newText = args[argi+1]
|
||||
|
||||
argi += 2
|
||||
|
||||
*pargi = argi
|
||||
if !doConstruct { // All transformers must do this for main command-line parsing
|
||||
return nil
|
||||
}
|
||||
|
||||
transformer, err := NewTransformerSub(
|
||||
fieldNames,
|
||||
oldText,
|
||||
newText,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return transformer
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
type TransformerSub struct {
|
||||
fieldNames []string
|
||||
oldText *mlrval.Mlrval
|
||||
newText *mlrval.Mlrval
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
func NewTransformerSub(
|
||||
fieldNames []string,
|
||||
oldText string,
|
||||
newText string,
|
||||
) (*TransformerSub, error) {
|
||||
tr := &TransformerSub{
|
||||
fieldNames: fieldNames,
|
||||
oldText: mlrval.FromString(oldText),
|
||||
newText: mlrval.FromString(newText),
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (tr *TransformerSub) Transform(
|
||||
inrecAndContext *types.RecordAndContext,
|
||||
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
|
||||
inputDownstreamDoneChannel <-chan bool,
|
||||
outputDownstreamDoneChannel chan<- bool,
|
||||
) {
|
||||
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
|
||||
|
||||
if !inrecAndContext.EndOfStream {
|
||||
inrec := inrecAndContext.Record
|
||||
|
||||
for _, fieldName := range tr.fieldNames {
|
||||
oldValue := inrec.Get(fieldName)
|
||||
if oldValue == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
newValue := bifs.BIF_sub(oldValue, tr.oldText, tr.newText)
|
||||
|
||||
inrec.PutReference(fieldName, newValue)
|
||||
}
|
||||
|
||||
outputRecordsAndContexts.PushBack(inrecAndContext)
|
||||
} else {
|
||||
outputRecordsAndContexts.PushBack(inrecAndContext) // emit end-of-stream marker
|
||||
}
|
||||
}
|
||||
355
pkg/transformers/subs.go
Normal file
355
pkg/transformers/subs.go
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
package transformers
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/johnkerl/miller/pkg/bifs"
|
||||
"github.com/johnkerl/miller/pkg/cli"
|
||||
"github.com/johnkerl/miller/pkg/lib"
|
||||
"github.com/johnkerl/miller/pkg/mlrval"
|
||||
"github.com/johnkerl/miller/pkg/types"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
const verbNameSub = "sub"
|
||||
const verbNameGsub = "gsub"
|
||||
const verbNameSsub = "ssub"
|
||||
|
||||
var SubSetup = TransformerSetup{
|
||||
Verb: verbNameSub,
|
||||
UsageFunc: transformerSubUsage,
|
||||
ParseCLIFunc: transformerSubParseCLI,
|
||||
IgnoresInput: false,
|
||||
}
|
||||
|
||||
var GsubSetup = TransformerSetup{
|
||||
Verb: verbNameGsub,
|
||||
UsageFunc: transformerGsubUsage,
|
||||
ParseCLIFunc: transformerGsubParseCLI,
|
||||
IgnoresInput: false,
|
||||
}
|
||||
|
||||
var SsubSetup = TransformerSetup{
|
||||
Verb: verbNameSsub,
|
||||
UsageFunc: transformerSsubUsage,
|
||||
ParseCLIFunc: transformerSsubParseCLI,
|
||||
IgnoresInput: false,
|
||||
}
|
||||
|
||||
func transformerSubUsage(
|
||||
o *os.File,
|
||||
) {
|
||||
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSub)
|
||||
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
|
||||
fmt.Fprintf(o, "for the old string and not handling multiple matches, like the `sub` DSL function.\n")
|
||||
fmt.Fprintf(o, "See also the `gsub` and `ssub` verbs.\n")
|
||||
fmt.Fprintf(o, "Options:\n")
|
||||
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
|
||||
fmt.Fprintf(o, "-h|--help Show this message.\n")
|
||||
}
|
||||
|
||||
func transformerGsubUsage(
|
||||
o *os.File,
|
||||
) {
|
||||
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameGsub)
|
||||
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), with regex support\n")
|
||||
fmt.Fprintf(o, "for the old string and handling multiple matches, like the `gsub` DSL function.\n")
|
||||
fmt.Fprintf(o, "See also the `sub` and `ssub` verbs.\n")
|
||||
fmt.Fprintf(o, "Options:\n")
|
||||
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
|
||||
fmt.Fprintf(o, "-h|--help Show this message.\n")
|
||||
}
|
||||
|
||||
func transformerSsubUsage(
|
||||
o *os.File,
|
||||
) {
|
||||
fmt.Fprintf(o, "Usage: %s %s [options]\n", "mlr", verbNameSsub)
|
||||
fmt.Fprintf(o, "Replaces old string with new string in specified field(s), without regex support for\n")
|
||||
fmt.Fprintf(o, "the old string, like the `ssub` DSL function. See also the `gsub` and `sub` verbs.\n")
|
||||
fmt.Fprintf(o, "Options:\n")
|
||||
fmt.Fprintf(o, "-f {a,b,c} Field names to convert.\n")
|
||||
fmt.Fprintf(o, "-h|--help Show this message.\n")
|
||||
}
|
||||
|
||||
type subConstructorFunc func(
|
||||
fieldNames []string,
|
||||
doAllFieldNames bool,
|
||||
doRegexes bool,
|
||||
oldText string,
|
||||
newText string,
|
||||
) (IRecordTransformer, error)
|
||||
|
||||
type fieldAcceptorFunc func(
|
||||
fieldName string,
|
||||
) bool
|
||||
|
||||
func transformerSubParseCLI(
|
||||
pargi *int,
|
||||
argc int,
|
||||
args []string,
|
||||
opts *cli.TOptions,
|
||||
doConstruct bool, // false for first pass of CLI-parse, true for second pass
|
||||
) IRecordTransformer {
|
||||
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSubUsage, NewTransformerSub)
|
||||
}
|
||||
|
||||
func transformerGsubParseCLI(
|
||||
pargi *int,
|
||||
argc int,
|
||||
args []string,
|
||||
opts *cli.TOptions,
|
||||
doConstruct bool, // false for first pass of CLI-parse, true for second pass
|
||||
) IRecordTransformer {
|
||||
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerGsubUsage, NewTransformerGsub)
|
||||
}
|
||||
|
||||
func transformerSsubParseCLI(
|
||||
pargi *int,
|
||||
argc int,
|
||||
args []string,
|
||||
opts *cli.TOptions,
|
||||
doConstruct bool, // false for first pass of CLI-parse, true for second pass
|
||||
) IRecordTransformer {
|
||||
return transformerSubsParseCLI(pargi, argc, args, opts, doConstruct, transformerSsubUsage, NewTransformerSsub)
|
||||
}
|
||||
|
||||
// transformerSubsParseCLI is a shared CLI-parser for the sub, gsub, and ssub verbs.
|
||||
func transformerSubsParseCLI(
|
||||
pargi *int,
|
||||
argc int,
|
||||
args []string,
|
||||
_ *cli.TOptions,
|
||||
doConstruct bool, // false for first pass of CLI-parse, true for second pass
|
||||
usageFunc TransformerUsageFunc,
|
||||
constructorFunc subConstructorFunc,
|
||||
) IRecordTransformer {
|
||||
|
||||
// Skip the verb name from the current spot in the mlr command line
|
||||
argi := *pargi
|
||||
verb := args[argi]
|
||||
argi++
|
||||
|
||||
// Parse local flags
|
||||
var fieldNames []string = nil
|
||||
doAllFieldNames := false
|
||||
doRegexes := false
|
||||
var oldText string
|
||||
var newText string
|
||||
|
||||
for argi < argc /* variable increment: 1 or 2 depending on flag */ {
|
||||
opt := args[argi]
|
||||
if !strings.HasPrefix(opt, "-") {
|
||||
break // No more flag options to process
|
||||
}
|
||||
if args[argi] == "--" {
|
||||
break // All transformers must do this so main-flags can follow verb-flags
|
||||
}
|
||||
argi++
|
||||
|
||||
if opt == "-h" || opt == "--help" {
|
||||
usageFunc(os.Stdout)
|
||||
os.Exit(0)
|
||||
|
||||
} else if opt == "-a" {
|
||||
doAllFieldNames = true
|
||||
doRegexes = false
|
||||
fieldNames = nil
|
||||
|
||||
} else if opt == "-r" {
|
||||
doRegexes = true
|
||||
|
||||
} else if opt == "-f" {
|
||||
fieldNames = cli.VerbGetStringArrayArgOrDie(verb, opt, args, &argi, argc)
|
||||
doAllFieldNames = false
|
||||
} else {
|
||||
usageFunc(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if fieldNames == nil && !doAllFieldNames {
|
||||
usageFunc(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the old and new text from the command line
|
||||
if (argc - argi) < 2 {
|
||||
usageFunc(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
oldText = args[argi]
|
||||
newText = args[argi+1]
|
||||
|
||||
argi += 2
|
||||
|
||||
*pargi = argi
|
||||
if !doConstruct { // All transformers must do this for main command-line parsing
|
||||
return nil
|
||||
}
|
||||
|
||||
transformer, err := constructorFunc(
|
||||
fieldNames,
|
||||
doAllFieldNames,
|
||||
doRegexes,
|
||||
oldText,
|
||||
newText,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return transformer
|
||||
}
|
||||
|
||||
type TransformerSubs struct {
|
||||
fieldNamesSet map[string]bool // for -f
|
||||
regexes []*regexp.Regexp // for -r
|
||||
oldText *mlrval.Mlrval
|
||||
newText *mlrval.Mlrval
|
||||
fieldAcceptor fieldAcceptorFunc // for -f, -r, -a
|
||||
subber bifs.TernaryFunc // for sub, gsub, ssub
|
||||
}
|
||||
|
||||
func NewTransformerSub(
|
||||
fieldNames []string,
|
||||
doAllFieldNames bool,
|
||||
doRegexes bool,
|
||||
oldText string,
|
||||
newText string,
|
||||
) (IRecordTransformer, error) {
|
||||
return NewTransformerSubs(fieldNames, doAllFieldNames, doRegexes, oldText, newText, safe_sub)
|
||||
}
|
||||
|
||||
func NewTransformerGsub(
|
||||
fieldNames []string,
|
||||
doAllFieldNames bool,
|
||||
doRegexes bool,
|
||||
oldText string,
|
||||
newText string,
|
||||
) (IRecordTransformer, error) {
|
||||
return NewTransformerSubs(fieldNames, doAllFieldNames, doRegexes, oldText, newText, safe_gsub)
|
||||
}
|
||||
|
||||
func NewTransformerSsub(
|
||||
fieldNames []string,
|
||||
doAllFieldNames bool,
|
||||
doRegexes bool,
|
||||
oldText string,
|
||||
newText string,
|
||||
) (IRecordTransformer, error) {
|
||||
return NewTransformerSubs(fieldNames, doAllFieldNames, doRegexes, oldText, newText, safe_ssub)
|
||||
}
|
||||
|
||||
func NewTransformerSubs(
|
||||
fieldNames []string,
|
||||
doAllFieldNames bool,
|
||||
doRegexes bool,
|
||||
oldText string,
|
||||
newText string,
|
||||
subber bifs.TernaryFunc,
|
||||
) (IRecordTransformer, error) {
|
||||
tr := &TransformerSubs{
|
||||
fieldNamesSet: lib.StringListToSet(fieldNames),
|
||||
oldText: mlrval.FromString(oldText),
|
||||
newText: mlrval.FromString(newText),
|
||||
subber: subber,
|
||||
}
|
||||
if doAllFieldNames {
|
||||
tr.fieldAcceptor = tr.fieldAcceptorAll
|
||||
} else if doRegexes {
|
||||
tr.fieldAcceptor = tr.fieldAcceptorByRegexes
|
||||
|
||||
tr.regexes = make([]*regexp.Regexp, len(fieldNames))
|
||||
for i, regexString := range fieldNames {
|
||||
// Handles "a.*b"i Miller case-insensitive-regex specification
|
||||
regex, err := lib.CompileMillerRegex(regexString)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s %s: cannot compile regex [%s]\n", "mlr", verbNameCut, regexString)
|
||||
os.Exit(1)
|
||||
}
|
||||
tr.regexes[i] = regex
|
||||
}
|
||||
} else {
|
||||
tr.fieldAcceptor = tr.fieldAcceptorByNames
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (tr *TransformerSubs) Transform(
|
||||
inrecAndContext *types.RecordAndContext,
|
||||
outputRecordsAndContexts *list.List, // list of *types.RecordAndContext
|
||||
inputDownstreamDoneChannel <-chan bool,
|
||||
outputDownstreamDoneChannel chan<- bool,
|
||||
) {
|
||||
HandleDefaultDownstreamDone(inputDownstreamDoneChannel, outputDownstreamDoneChannel)
|
||||
|
||||
if !inrecAndContext.EndOfStream {
|
||||
inrec := inrecAndContext.Record
|
||||
// Run sub, gsub, or ssub on the user-specified field names
|
||||
for pe := inrec.Head; pe != nil; pe = pe.Next {
|
||||
if tr.fieldAcceptor(pe.Key) {
|
||||
pe.Value = tr.subber(pe.Value, tr.oldText, tr.newText)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Including emit of end-of-stream marker
|
||||
outputRecordsAndContexts.PushBack(inrecAndContext)
|
||||
}
|
||||
|
||||
// fieldAcceptorByNames implements -f
|
||||
func (tr *TransformerSubs) fieldAcceptorByNames(
|
||||
fieldName string,
|
||||
) bool {
|
||||
return tr.fieldNamesSet[fieldName]
|
||||
}
|
||||
|
||||
// fieldAcceptorByNames implements -r
|
||||
func (tr *TransformerSubs) fieldAcceptorByRegexes(
|
||||
fieldName string,
|
||||
) bool {
|
||||
for _, regex := range tr.regexes {
|
||||
if regex.MatchString(fieldName) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// fieldAcceptorByNames implements -a
|
||||
func (tr *TransformerSubs) fieldAcceptorAll(
|
||||
fieldName string,
|
||||
) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// safe_sub implements sub, but doesn't produce error-type on non-string input.
|
||||
func safe_sub(input1, input2, input3 *mlrval.Mlrval) *mlrval.Mlrval {
|
||||
if input1.IsString() {
|
||||
return bifs.BIF_sub(input1, input2, input3)
|
||||
} else {
|
||||
return input1
|
||||
}
|
||||
}
|
||||
|
||||
// safe_gsub implements gsub, but doesn't produce error-type on non-string input.
|
||||
func safe_gsub(input1, input2, input3 *mlrval.Mlrval) *mlrval.Mlrval {
|
||||
if input1.IsString() {
|
||||
return bifs.BIF_gsub(input1, input2, input3)
|
||||
} else {
|
||||
return input1
|
||||
}
|
||||
}
|
||||
|
||||
// safe_ssub implements ssub, but doesn't produce error-type on non-string input.
|
||||
func safe_ssub(input1, input2, input3 *mlrval.Mlrval) *mlrval.Mlrval {
|
||||
if input1.IsString() {
|
||||
return bifs.BIF_ssub(input1, input2, input3)
|
||||
} else {
|
||||
return input1
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
mlr --d2p --from test/input/abixy sub -f a,b e X
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
a b i x y
|
||||
pan pan 1 0.34679014 0.72680286
|
||||
Xks pan 2 0.75867996 0.52215111
|
||||
wyX wyX 3 0.20460331 0.33831853
|
||||
Xks wyX 4 0.38139939 0.13418874
|
||||
wyX pan 5 0.57328892 0.86362447
|
||||
zXe pan 6 0.52712616 0.49322129
|
||||
Xks zXe 7 0.61178406 0.18788492
|
||||
zXe wyX 8 0.59855401 0.97618139
|
||||
hat wyX 9 0.03144188 0.74955076
|
||||
pan wyX 10 0.50262601 0.95261836
|
||||
|
|
@ -1 +0,0 @@
|
|||
mlr --d2p --from test/input/abixy gsub -f a,b e X
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
a b i x y
|
||||
pan pan 1 0.34679014 0.72680286
|
||||
Xks pan 2 0.75867996 0.52215111
|
||||
wyX wyX 3 0.20460331 0.33831853
|
||||
Xks wyX 4 0.38139939 0.13418874
|
||||
wyX pan 5 0.57328892 0.86362447
|
||||
zXX pan 6 0.52712616 0.49322129
|
||||
Xks zXX 7 0.61178406 0.18788492
|
||||
zXX wyX 8 0.59855401 0.97618139
|
||||
hat wyX 9 0.03144188 0.74955076
|
||||
pan wyX 10 0.50262601 0.95261836
|
||||
|
|
@ -1 +0,0 @@
|
|||
mlr --d2p --from test/input/abixy sub -f a,b . X
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
a b i x y
|
||||
Xan Xan 1 0.34679014 0.72680286
|
||||
Xks Xan 2 0.75867996 0.52215111
|
||||
Xye Xye 3 0.20460331 0.33831853
|
||||
Xks Xye 4 0.38139939 0.13418874
|
||||
Xye Xan 5 0.57328892 0.86362447
|
||||
Xee Xan 6 0.52712616 0.49322129
|
||||
Xks Xee 7 0.61178406 0.18788492
|
||||
Xee Xye 8 0.59855401 0.97618139
|
||||
Xat Xye 9 0.03144188 0.74955076
|
||||
Xan Xye 10 0.50262601 0.95261836
|
||||
|
|
@ -1 +0,0 @@
|
|||
mlr --d2p --from test/input/abixy ssub -f a,b e X
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
a b i x y
|
||||
pan pan 1 0.34679014 0.72680286
|
||||
Xks pan 2 0.75867996 0.52215111
|
||||
wyX wyX 3 0.20460331 0.33831853
|
||||
Xks wyX 4 0.38139939 0.13418874
|
||||
wyX pan 5 0.57328892 0.86362447
|
||||
zXe pan 6 0.52712616 0.49322129
|
||||
Xks zXe 7 0.61178406 0.18788492
|
||||
zXe wyX 8 0.59855401 0.97618139
|
||||
hat wyX 9 0.03144188 0.74955076
|
||||
pan wyX 10 0.50262601 0.95261836
|
||||
1
test/cases/verb-sub-gsub-ssub/gsub-a/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/gsub-a/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv gsub -a l X
|
||||
11
test/cases/verb-sub-gsub-ssub/gsub-a/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/gsub-a/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yeXXow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square faXse 4 48 77.55420000 7.46700000
|
||||
purpXe triangXe faXse 5 51 81.22900000 8.59100000
|
||||
red square faXse 6 64 77.19910000 9.53100000
|
||||
purpXe triangXe faXse 7 65 80.14050000 5.82400000
|
||||
yeXXow circXe true 8 73 63.97850000 4.23700000
|
||||
yeXXow circXe true 9 87 63.50580000 8.33500000
|
||||
purpXe square faXse 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/gsub-f/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/gsub-f/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv gsub -f color,shape,index l X
|
||||
11
test/cases/verb-sub-gsub-ssub/gsub-f/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/gsub-f/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yeXXow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square false 4 48 77.55420000 7.46700000
|
||||
purpXe triangXe false 5 51 81.22900000 8.59100000
|
||||
red square false 6 64 77.19910000 9.53100000
|
||||
purpXe triangXe false 7 65 80.14050000 5.82400000
|
||||
yeXXow circXe true 8 73 63.97850000 4.23700000
|
||||
yeXXow circXe true 9 87 63.50580000 8.33500000
|
||||
purpXe square false 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/non-windows/gsub-r/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/non-windows/gsub-r/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv gsub -r -f '.*e' l X
|
||||
11
test/cases/verb-sub-gsub-ssub/non-windows/gsub-r/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/non-windows/gsub-r/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yellow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square false 4 48 77.55420000 7.46700000
|
||||
purple triangXe false 5 51 81.22900000 8.59100000
|
||||
red square false 6 64 77.19910000 9.53100000
|
||||
purple triangXe false 7 65 80.14050000 5.82400000
|
||||
yellow circXe true 8 73 63.97850000 4.23700000
|
||||
yellow circXe true 9 87 63.50580000 8.33500000
|
||||
purple square false 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/non-windows/ssub-r/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/non-windows/ssub-r/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv ssub -r -f '.*e' l X
|
||||
11
test/cases/verb-sub-gsub-ssub/non-windows/ssub-r/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/non-windows/ssub-r/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yellow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square false 4 48 77.55420000 7.46700000
|
||||
purple triangXe false 5 51 81.22900000 8.59100000
|
||||
red square false 6 64 77.19910000 9.53100000
|
||||
purple triangXe false 7 65 80.14050000 5.82400000
|
||||
yellow circXe true 8 73 63.97850000 4.23700000
|
||||
yellow circXe true 9 87 63.50580000 8.33500000
|
||||
purple square false 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/non-windows/sub-r/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/non-windows/sub-r/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv sub -r -f '.*e' l X
|
||||
0
test/cases/verb-sub-gsub-ssub/non-windows/sub-r/experr
Normal file
0
test/cases/verb-sub-gsub-ssub/non-windows/sub-r/experr
Normal file
11
test/cases/verb-sub-gsub-ssub/non-windows/sub-r/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/non-windows/sub-r/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yellow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square false 4 48 77.55420000 7.46700000
|
||||
purple triangXe false 5 51 81.22900000 8.59100000
|
||||
red square false 6 64 77.19910000 9.53100000
|
||||
purple triangXe false 7 65 80.14050000 5.82400000
|
||||
yellow circXe true 8 73 63.97850000 4.23700000
|
||||
yellow circXe true 9 87 63.50580000 8.33500000
|
||||
purple square false 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/ssub-a/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/ssub-a/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv ssub -a l X
|
||||
0
test/cases/verb-sub-gsub-ssub/ssub-a/experr
Normal file
0
test/cases/verb-sub-gsub-ssub/ssub-a/experr
Normal file
11
test/cases/verb-sub-gsub-ssub/ssub-a/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/ssub-a/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yeXlow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square faXse 4 48 77.55420000 7.46700000
|
||||
purpXe triangXe faXse 5 51 81.22900000 8.59100000
|
||||
red square faXse 6 64 77.19910000 9.53100000
|
||||
purpXe triangXe faXse 7 65 80.14050000 5.82400000
|
||||
yeXlow circXe true 8 73 63.97850000 4.23700000
|
||||
yeXlow circXe true 9 87 63.50580000 8.33500000
|
||||
purpXe square faXse 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/ssub-f/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/ssub-f/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv ssub -f color,shape,index l X
|
||||
0
test/cases/verb-sub-gsub-ssub/ssub-f/experr
Normal file
0
test/cases/verb-sub-gsub-ssub/ssub-f/experr
Normal file
11
test/cases/verb-sub-gsub-ssub/ssub-f/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/ssub-f/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yeXlow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square false 4 48 77.55420000 7.46700000
|
||||
purpXe triangXe false 5 51 81.22900000 8.59100000
|
||||
red square false 6 64 77.19910000 9.53100000
|
||||
purpXe triangXe false 7 65 80.14050000 5.82400000
|
||||
yeXlow circXe true 8 73 63.97850000 4.23700000
|
||||
yeXlow circXe true 9 87 63.50580000 8.33500000
|
||||
purpXe square false 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/sub-a/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/sub-a/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv sub -a l X
|
||||
0
test/cases/verb-sub-gsub-ssub/sub-a/experr
Normal file
0
test/cases/verb-sub-gsub-ssub/sub-a/experr
Normal file
11
test/cases/verb-sub-gsub-ssub/sub-a/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/sub-a/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yeXlow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square faXse 4 48 77.55420000 7.46700000
|
||||
purpXe triangXe faXse 5 51 81.22900000 8.59100000
|
||||
red square faXse 6 64 77.19910000 9.53100000
|
||||
purpXe triangXe faXse 7 65 80.14050000 5.82400000
|
||||
yeXlow circXe true 8 73 63.97850000 4.23700000
|
||||
yeXlow circXe true 9 87 63.50580000 8.33500000
|
||||
purpXe square faXse 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/sub-f-2/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/sub-f-2/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv sub -f a,b l X
|
||||
0
test/cases/verb-sub-gsub-ssub/sub-f-2/experr
Normal file
0
test/cases/verb-sub-gsub-ssub/sub-f-2/experr
Normal file
11
test/cases/verb-sub-gsub-ssub/sub-f-2/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/sub-f-2/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yellow triangle true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circle true 3 16 13.81030000 2.90100000
|
||||
red square false 4 48 77.55420000 7.46700000
|
||||
purple triangle false 5 51 81.22900000 8.59100000
|
||||
red square false 6 64 77.19910000 9.53100000
|
||||
purple triangle false 7 65 80.14050000 5.82400000
|
||||
yellow circle true 8 73 63.97850000 4.23700000
|
||||
yellow circle true 9 87 63.50580000 8.33500000
|
||||
purple square false 10 91 72.37350000 8.24300000
|
||||
1
test/cases/verb-sub-gsub-ssub/sub-f/cmd
Normal file
1
test/cases/verb-sub-gsub-ssub/sub-f/cmd
Normal file
|
|
@ -0,0 +1 @@
|
|||
mlr --c2p --from test/input/example.csv sub -f color,shape,index l X
|
||||
0
test/cases/verb-sub-gsub-ssub/sub-f/experr
Normal file
0
test/cases/verb-sub-gsub-ssub/sub-f/experr
Normal file
11
test/cases/verb-sub-gsub-ssub/sub-f/expout
Normal file
11
test/cases/verb-sub-gsub-ssub/sub-f/expout
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
color shape flag k index quantity rate
|
||||
yeXlow triangXe true 1 11 43.64980000 9.88700000
|
||||
red square true 2 15 79.27780000 0.01300000
|
||||
red circXe true 3 16 13.81030000 2.90100000
|
||||
red square false 4 48 77.55420000 7.46700000
|
||||
purpXe triangXe false 5 51 81.22900000 8.59100000
|
||||
red square false 6 64 77.19910000 9.53100000
|
||||
purpXe triangXe false 7 65 80.14050000 5.82400000
|
||||
yeXlow circXe true 8 73 63.97850000 4.23700000
|
||||
yeXlow circXe true 9 87 63.50580000 8.33500000
|
||||
purpXe square false 10 91 72.37350000 8.24300000
|
||||
Loading…
Add table
Add a link
Reference in a new issue