Skip to content

Commit

Permalink
add snip command
Browse files Browse the repository at this point in the history
  • Loading branch information
tamada committed Aug 5, 2023
1 parent ae0a880 commit 86113a8
Show file tree
Hide file tree
Showing 13 changed files with 395 additions and 33 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ build: setup
@$(call _buildSubcommand,ptake)
@$(call _buildSubcommand,ptest)
@$(call _buildSubcommand,puniq)
@$(call _buildSubcommand,snip)


lint: setup format
$(GO) vet $$(go list ./...)
Expand All @@ -43,6 +45,7 @@ define _createDist
GOOS=$(1) GOARCH=$(2) go build -tags $(1) -o dist/$(1)-$(2)/$(DIST)/bin/ptake$(3) cmd/ptake/*.go
GOOS=$(1) GOARCH=$(2) go build -tags $(1) -o dist/$(1)-$(2)/$(DIST)/bin/ptest$(3) cmd/ptest/*.go
GOOS=$(1) GOARCH=$(2) go build -tags $(1) -o dist/$(1)-$(2)/$(DIST)/bin/puniq$(3) cmd/puniq/*.go
GOOS=$(1) GOARCH=$(2) go build -tags $(1) -o dist/$(1)-$(2)/$(DIST)/bin/snip$(3) cmd/snip/*.go
tar cfz dist/$(DIST)-$(1)-$(2).tar.gz -C dist/$(1)-$(2) $(DIST)
echo "done"
endef
Expand Down
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

[![Homebrew](https://img.shields.io/badge/Homebrew-tamada/brew/peripherals-yellow?logo=homebrew)](https://github.com/tamada/btmeister/releases/tag/v0.9.6)


peripheral utility commands for the shell by contrasting [GNU coreutils](https://www.gnu.org/software/coreutils/).

## How to install
Expand All @@ -18,7 +19,7 @@ This project supports Homebrew.


```sh
brew tap tamada/brew
brew tap tamada/tap
brew install peripherals
```

Expand All @@ -28,9 +29,12 @@ brew install peripherals
* [`ptake`](#ptake)
* [`pskip`](#pskip)
* [`ptest`](#ptest)
* [`snip`](#snip)

### `puniq`

This command is similar to `uniq` command, however, `puniq` removes not adjacent duplicated lines.

```sh
puniq [OPTIONS] [INPUT [OUTPUT]]
OPTIONS
Expand All @@ -46,6 +50,9 @@ OUTPUT represents the destination.
### `ptake`
`ptake` command is similar to `head` command.
The additional feature is that `ptake` can take lines until the specified keyword is appeared.
```sh
ptake [OPTIONS] [FILEs...]
OPTIONS
Expand All @@ -67,6 +74,9 @@ FILE
### `pskip`
`pskip` command skips the first specified line/bytes.
Of course, `pskip` command can also accept the keyword for skipping lines until appearing it.
```shell
skip [OPTIONS] [FILEs...]
OPTIONS
Expand All @@ -88,6 +98,10 @@ FILE
### `ptest`
`ptest` command is similar to `test` command.
`ptake` and `pskip` commands use this command for `while` option.
```shell
ptest <expression>
file operation
Expand Down Expand Up @@ -142,6 +156,34 @@ combined operation
( expression ) true if expression is true
```
### `snip`
`snip` command is synthesis of `head` and `tail` command.
```shell
snip [OPTIONS] [FILEs...]
OPTIONS
-H, --head int print first HEAD lines (same as head command). (default -1)
-T, --tail int print last TAIL lines (same as tail command). (default -1)
-N, --number int print first and last lines (default is 5). (default 5)
-n, --line-number print line number with output lines.
-s, --no-snip-sign suppress printing of snip sign and the number of snipped lines.
-q, --no-header suppress printing of headers when multiple files are being examined.
-h, --help print this message and exit
-v, --version print the version information and exit
FILE
gives file name for the input. if this argument is single dash ('-') or absent,
it reads strings from STDIN.
if more than a single file is specified, each file is separated by a header
consisting of the string '==> XXX <==' where 'XXX' is the name of the file.
```
## License
[![License](https://img.shields.io/badge/License-MIT-green)](https://github.com/tamada/peripherals/blob/main/LICENSE)
## Logo
![Logo](https://raw.githubusercontent.com/tamada/peripherals/main/site/static/images/logo.svg)
41 changes: 25 additions & 16 deletions cmd/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,40 @@ package common

import (
"fmt"

"github.com/bits-and-blooms/bitset"
"github.com/tamada/peripherals/ptake"
)

type Options struct {
Lines int
Bytes int
Keyword string
Predicate string
NoHeader bool
type HelpOptions struct {
HelpFlag bool
VersionFlag bool
TType ptake.TakerType
}

func (opts *HelpOptions) IsHelp() bool {
return opts.HelpFlag || opts.VersionFlag
}

type HeaderOpts struct {
NoHeader bool
}

func (opts *HeaderOpts) IsPrintHeader(args []string) bool {
return !opts.NoHeader && len(args) > 1
}

type Options struct {
Lines int
Bytes int
Keyword string
Predicate string
TType ptake.TakerType
HeaderOpts
HelpOptions
}

func New() *Options {
return &Options{Lines: -1, Bytes: -1, Keyword: "", Predicate: "", NoHeader: false}
return &Options{Lines: -1, Bytes: -1, Keyword: "", Predicate: ""}
}

func (opts *Options) Int() int {
Expand All @@ -39,10 +56,6 @@ func (opts *Options) String() string {
return ""
}

func (opts *Options) IsPrintHeader(args []string) bool {
return !opts.NoHeader && len(args) > 1
}

func (opts *Options) Validate() error {
if opts.HelpFlag {
return nil
Expand Down Expand Up @@ -73,10 +86,6 @@ func (opts *Options) Validate() error {
return nil
}

func (opts *Options) IsHelp() bool {
return opts.HelpFlag || opts.VersionFlag
}

func PrintError(err error, statusOnError int) int {
if err == nil {
return 0
Expand Down
4 changes: 2 additions & 2 deletions cmd/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package common
import "testing"

func TestValidate(t *testing.T) {
opts1 := Options{Lines: 0, Bytes: 0, Keyword: "", Predicate: "", NoHeader: false}
opts1 := Options{Lines: 0, Bytes: 0, Keyword: "", Predicate: ""}
if err := opts1.Validate(); err == nil {
t.Errorf("specifying no option causes an error")
}
opts2 := Options{Lines: 10, Bytes: 10, Keyword: "", Predicate: "", NoHeader: false}
opts2 := Options{Lines: 10, Bytes: 10, Keyword: "", Predicate: ""}
if err := opts2.Validate(); err == nil {
t.Errorf("specifying no option causes an error")
}
Expand Down
101 changes: 101 additions & 0 deletions cmd/snip/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package main

import (
"errors"
"fmt"
"os"

flag "github.com/spf13/pflag"
"github.com/tamada/peripherals"
"github.com/tamada/peripherals/cmd/common"
"github.com/tamada/peripherals/errs"
"github.com/tamada/peripherals/snip"
)

type SnipOpts struct {
Number int
snip.Snipper
common.HeaderOpts
common.HelpOptions
}

func (opts *SnipOpts) Validate() error {
if opts.Head < 0 && opts.Tail < 0 && opts.Number < 0 {
return errors.New("no lines specified. either options of -H, -T, -N must be specified")
}
if opts.Head < 0 && opts.Tail < 0 && opts.Number > 0 {
opts.Head = opts.Number
opts.Tail = opts.Number
}
return nil
}

func helpMessage(appName string, fs *flag.FlagSet) string {
return fmt.Sprintf(`%s [OPTIONS] [FILEs...]
OPTIONS
%s
FILE
gives file name for the input. if this argument is single dash ('-') or absent,
it reads strings from STDIN.
if more than a single file is specified, each file is separated by a header
consisting of the string '==> XXX <==' where 'XXX' is the name of the file.`, appName, fs.FlagUsages())
}

func buildFlags() (*flag.FlagSet, *SnipOpts) {
var opts = &SnipOpts{}
var flags = flag.NewFlagSet("pskip", flag.ContinueOnError)
flags.Usage = func() { fmt.Println(helpMessage("pskip", flags)) }
flags.IntVarP(&opts.Head, "head", "H", -1, "print first HEAD lines (same as head command).")
flags.IntVarP(&opts.Tail, "tail", "T", -1, "print last TAIL lines (same as tail command).")
flags.IntVarP(&opts.Number, "number", "N", 5, "print first and last lines (default is 5).")
flags.BoolVarP(&opts.LineNumber, "line-number", "n", false, "print line number with output lines.")
flags.BoolVarP(&opts.SkipSnip, "no-snip-sign", "s", false, "suppress printing of snip sign and the number of snipped lines.")
flags.BoolVarP(&opts.NoHeader, "no-header", "q", false, "suppress printing of headers when multiple files are being examined.")
flags.BoolVarP(&opts.HelpFlag, "help", "h", false, "print this message and exit")
flags.BoolVarP(&opts.VersionFlag, "version", "v", false, "print the version information and exit")
flags.SortFlags = false
return flags, opts
}

func perform(opts *SnipOpts, args []string) error {
if err := opts.Validate(); err != nil {
return err
}
center := errs.New()
for _, arg := range args {
if opts.IsPrintHeader(args) {
fmt.Printf("===> %s <===\n", arg)
}
center.Push(opts.PerformEach(arg, os.Stdout))
}
if len(args) == 0 {
center.Push(opts.PerformEach("", os.Stdout))
}
return center.SelfOrNil()
}

func printHelp(opts *SnipOpts, fs *flag.FlagSet) int {
if opts.VersionFlag {
fmt.Println(peripherals.Version("snip"))
}
if opts.HelpFlag {
fmt.Println(helpMessage("snip", fs))
}
return 0
}

func goMain(args []string) int {
flags, opts := buildFlags()
if err := flags.Parse(args); err != nil {
return common.PrintError(err, 1)
}
if opts.IsHelp() {
return printHelp(opts, flags)
}
return common.PrintError(perform(opts, flags.Args()[1:]), 2)
}

func main() {
status := goMain(os.Args)
os.Exit(status)
}
49 changes: 49 additions & 0 deletions cmd/snip/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

func Example_withNumber() {
goMain([]string{"snip", "--line-number", "--number", "2", "../../testdata/test1.txt"})
// Output:
// 1 a1
// 2 a1
// ... snip ... (4 lines)
// 7 a1
// 8 A1
}

func Example_headTail() {
goMain([]string{"snip", "--no-snip-sign", "--head", "1", "--tail", "2", "../../testdata/test1.txt"})
// Output:
// a1
// a1
// A1
}

func Example_ShortLengthFile() {
goMain([]string{"snip", "../../testdata/eval_script.sh"}) // 4 lines
// Output:
// #! /bin/sh
//
// echo "PLINE: $PLINE"
// exec test "$PLINE" = "a1"
}

func Example_printHelp() {
goMain([]string{"snip", "--help"})
// Output:
// snip [OPTIONS] [FILEs...]
// OPTIONS
// -H, --head int print first HEAD lines (same as head command). (default -1)
// -T, --tail int print last TAIL lines (same as tail command). (default -1)
// -N, --number int print first and last lines (default is 5). (default 5)
// -n, --line-number print line number with output lines.
// -s, --no-snip-sign suppress printing of snip sign and the number of snipped lines.
// -q, --no-header suppress printing of headers when multiple files are being examined.
// -h, --help print this message and exit
// -v, --version print the version information and exit
//
// FILE
// gives file name for the input. if this argument is single dash ('-') or absent,
// it reads strings from STDIN.
// if more than a single file is specified, each file is separated by a header
// consisting of the string '==> XXX <==' where 'XXX' is the name of the file.
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ go 1.18

require (
github.com/bits-and-blooms/bitset v1.2.2
github.com/gertd/go-pluralize v0.2.1
github.com/spf13/pflag v1.0.5
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
github.com/bits-and-blooms/bitset v1.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk=
github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
3 changes: 2 additions & 1 deletion site/content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ This product includes the following commands.
* skip lines while satisfying the predicate.
* [`ptest`](usage#ptest)
* another implementation of `test` command.

* [`snip`](usage#snip)
* synthesis of `head` and `tail` command.

Loading

0 comments on commit 86113a8

Please sign in to comment.