Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
10de418b6e | |||
929784da83 | |||
|
e7a2a65eb6 | ||
|
75dce36679 | ||
|
2f28cef4b5 | ||
|
78e901eccf | ||
|
b849cd47b1 | ||
|
5bf2f81d98 | ||
|
50d1ac83b6 |
29
.gitignore
vendored
29
.gitignore
vendored
@ -1 +1,30 @@
|
|||||||
format
|
format
|
||||||
|
format_freebsd_386
|
||||||
|
format_linux_amd64
|
||||||
|
format_linux_arm
|
||||||
|
format_openbsd_amd64
|
||||||
|
format_linux_mips64
|
||||||
|
format_freebsd_arm
|
||||||
|
format_netbsd_arm64
|
||||||
|
format_windows_arm64
|
||||||
|
format_netbsd_386
|
||||||
|
format_freebsd_arm64
|
||||||
|
format_linux_386
|
||||||
|
format_openbsd_386
|
||||||
|
format_openbsd_mips64
|
||||||
|
format_netbsd_arm
|
||||||
|
format_linux_ppc64le
|
||||||
|
format_windows_386
|
||||||
|
format_linux_arm64
|
||||||
|
format_netbsd_amd64
|
||||||
|
format_windows_arm
|
||||||
|
format_linux_mips64le
|
||||||
|
format_openbsd_arm
|
||||||
|
format_windows_amd64
|
||||||
|
format_linux_riscv64
|
||||||
|
format_linux_ppc64
|
||||||
|
format_linux_mipsle
|
||||||
|
format_linux_mips
|
||||||
|
format_openbsd_arm64
|
||||||
|
format_freebsd_amd64
|
||||||
|
format_linux_s390x
|
||||||
|
19
LICENSE.md
Normal file
19
LICENSE.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2022 Mila Ringwald
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
130
README.md
130
README.md
@ -1,13 +1,27 @@
|
|||||||
# format
|
# format
|
||||||
|
|
||||||
format greps lines from stdin, parses them via regex and reformats them according to a given format string
|
## Source code
|
||||||
|
|
||||||
## Input pattern
|
You can find the source code here: https://git.milar.in/milarin/format
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If you have Go installed, you can simply go install the program: `go install git.milar.in/milarin/format@latest`
|
||||||
|
|
||||||
|
There are pre-compiled executables for various platforms on the [repository](https://git.milar.in/milarin/format/releases).
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Distributed under the MIT License. See [LICENSE.md](https://git.milar.in/milarin/format/src/branch/main/LICENSE.md)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Input pattern
|
||||||
|
|
||||||
The input pattern describes the format in which the lines are parsed from stdin.
|
The input pattern describes the format in which the lines are parsed from stdin.
|
||||||
This pattern is a regular expression according to [Go's regexp spec](https://pkg.go.dev/regexp).
|
This pattern is a regular expression according to [Go's regexp spec](https://pkg.go.dev/regexp).
|
||||||
|
|
||||||
Be default, the input pattern will only be applied to every single line.
|
By default, the input pattern will only be applied to every single line.
|
||||||
When using multiline patterns, you can provide an amount of lines using the command line argument `-n` followed by an integer amount of lines.
|
When using multiline patterns, you can provide an amount of lines using the command line argument `-n` followed by an integer amount of lines.
|
||||||
|
|
||||||
Use subgroups for extracting specific parts of the input line.
|
Use subgroups for extracting specific parts of the input line.
|
||||||
@ -16,13 +30,15 @@ Provide your custom input pattern with the command line argument `-i '<pattern>'
|
|||||||
|
|
||||||
The default value is `^.*?$` which simply matches the whole line.
|
The default value is `^.*?$` which simply matches the whole line.
|
||||||
|
|
||||||
## Output pattern
|
### Output pattern
|
||||||
|
|
||||||
The output pattern describes the format in which lines are generated for stdout using data from the input pattern.
|
The output pattern describes the format in which lines are generated for stdout using data from the input pattern.
|
||||||
|
|
||||||
|
Provide an output pattern via `-o '<pattern>'`.
|
||||||
|
|
||||||
The default value is `{0}` which always matches the full input pattern
|
The default value is `{0}` which always matches the full input pattern
|
||||||
|
|
||||||
### Capturing groups
|
#### Capturing groups
|
||||||
|
|
||||||
Use the `{<group_index>}` syntax to use a specific capturing group.
|
Use the `{<group_index>}` syntax to use a specific capturing group.
|
||||||
- `{0}` always matches the whole line.
|
- `{0}` always matches the whole line.
|
||||||
@ -30,22 +46,39 @@ Use the `{<group_index>}` syntax to use a specific capturing group.
|
|||||||
- `{2}` matches the second capturing group
|
- `{2}` matches the second capturing group
|
||||||
- and so on
|
- and so on
|
||||||
|
|
||||||
### Formatting
|
#### Coloring
|
||||||
|
|
||||||
When referencing capturing groups, you can add a specific format for some data types as well using a simplified printf syntax.
|
Coloring a reference to a capturing group can be done via `{1:<color>}`.
|
||||||
|
|
||||||
You can use them using this syntax: `{1:%d}`. It will parse the first capturing group into an integer to get rid of leading zeros. Additionally, you can provide a given amount of leading zeros via: `{1:%03d}` for a total length of 3 digits.
|
`<color>` can be one of the following values:
|
||||||
|
|
||||||
|
- `black`
|
||||||
|
- `red`
|
||||||
|
- `green`
|
||||||
|
- `yellow`
|
||||||
|
- `blue`
|
||||||
|
- `magenta`
|
||||||
|
- `cyan`
|
||||||
|
- `white`
|
||||||
|
|
||||||
|
Leaving the color argument empty results into the default color.
|
||||||
|
|
||||||
|
#### Formatting
|
||||||
|
|
||||||
|
When referencing capturing groups, you can add a specific format for some data types using a simplified printf syntax.
|
||||||
|
|
||||||
|
You can use them using this syntax: `{1::%d}`. It will parse the first capturing group into an integer to get rid of leading zeros. Additionally, you can provide a given amount of leading zeros via: `{1::%03d}` for a total length of 3 digits.
|
||||||
|
|
||||||
The same method can also be applied to `%f` to further format floating point values.
|
The same method can also be applied to `%f` to further format floating point values.
|
||||||
See a full list of formatting options at [Go's fmt spec](https://pkg.go.dev/fmt).
|
See a full list of formatting options at [Go's fmt spec](https://pkg.go.dev/fmt).
|
||||||
|
|
||||||
Currently only `%s`, `%d`, `%f` and `%g` are supported though.
|
Currently only `%s`, `%d`, `%f` and `%g` are supported though.
|
||||||
|
|
||||||
### Mutators
|
#### Mutators
|
||||||
|
|
||||||
Mutators are a simple way of manipulating number values like integers and floats using a simple math-like expression
|
Mutators are a simple way of manipulating number values like integers and floats using a simple math-like expression
|
||||||
|
|
||||||
You can provide a mutator using the syntax: `{1:%d:+1}`. It will parse the first capturing group into an integer, adds 1 and then formats the result using the printf format `%d`.
|
You can provide a mutator using the syntax: `{1::%d:+1}`. It will parse the first capturing group into an integer, adds 1 and then formats the result using the printf format `%d`.
|
||||||
|
|
||||||
A mutator always consists of an operator and a value. In the example above `+` is the operator and `1` is the value.
|
A mutator always consists of an operator and a value. In the example above `+` is the operator and `1` is the value.
|
||||||
|
|
||||||
@ -55,13 +88,13 @@ The following operators are supported for `%d`, `%f` and `%g` formats:
|
|||||||
- `*`
|
- `*`
|
||||||
- `/`
|
- `/`
|
||||||
|
|
||||||
It is possible to add multiple mutators by just concatenating them: `{1:%d:*2+1}`.
|
It is possible to add multiple mutators by just concatenating them: `{1::%d:*2+1}`.
|
||||||
|
|
||||||
Multiple mutators will not follow any order of operations. They are simply applied from left to right!
|
**Multiple mutators will not follow any order of operations. They are simply applied from left to right!**
|
||||||
|
|
||||||
Furthermore you can reference caputring groups which will be parsed as the same type to apply its value. This is done via the following syntax: `{1:%d:+(2)}`. It will parse the first and second capturing group into integers and adds them.
|
Furthermore you can reference caputring groups which will be parsed as the same type to apply its value. This is done via the following syntax: `{1::%d:+(2)}`. It will parse the first and second capturing group into integers and adds them.
|
||||||
|
|
||||||
## Handling unmatched lines
|
### Handling unmatched lines
|
||||||
|
|
||||||
By default, lines which do not match the input pattern will be dropped.
|
By default, lines which do not match the input pattern will be dropped.
|
||||||
Use `-k` to keep them unchanged. They will be copied into stdout.
|
Use `-k` to keep them unchanged. They will be copied into stdout.
|
||||||
@ -69,8 +102,10 @@ Use `-k` to keep them unchanged. They will be copied into stdout.
|
|||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
### Copying
|
### Copying
|
||||||
|
Copying is the default behavior of `format`
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
```
|
```txt
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
@ -83,7 +118,7 @@ format
|
|||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
@ -95,7 +130,7 @@ Output:
|
|||||||
Only keep lines which contains an `i`
|
Only keep lines which contains an `i`
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
```
|
```txt
|
||||||
one
|
one
|
||||||
two
|
two
|
||||||
three
|
three
|
||||||
@ -114,7 +149,7 @@ format -i '.*i.*'
|
|||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
five
|
five
|
||||||
six
|
six
|
||||||
eight
|
eight
|
||||||
@ -123,9 +158,10 @@ nine
|
|||||||
|
|
||||||
|
|
||||||
### Removing leading zeros
|
### Removing leading zeros
|
||||||
|
Use printf syntax on a capturing group in the output pattern
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
```
|
```txt
|
||||||
001
|
001
|
||||||
002
|
002
|
||||||
003
|
003
|
||||||
@ -134,11 +170,11 @@ Input:
|
|||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
format -i '\d+' -o '{0:%d}'
|
format -i '\d+' -o '{0::%d}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
1
|
1
|
||||||
2
|
2
|
||||||
3
|
3
|
||||||
@ -148,7 +184,7 @@ Output:
|
|||||||
### Extracting dates
|
### Extracting dates
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
```
|
```txt
|
||||||
2022-04-18
|
2022-04-18
|
||||||
1970-01-01
|
1970-01-01
|
||||||
2006-01-02
|
2006-01-02
|
||||||
@ -156,22 +192,21 @@ Input:
|
|||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
format -i '(\d{4})-(\d{2})-(\d{2})' -o 'day: {3:%d} | month: {2:%d} | year: {1}'
|
format -i '(\d{4})-(\d{2})-(\d{2})' -o 'day: {3::%d} | month: {2::%d} | year: {1}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
day: 18 | month: 4 | year: 2022
|
day: 18 | month: 4 | year: 2022
|
||||||
day: 1 | month: 1 | year: 1970
|
day: 1 | month: 1 | year: 1970
|
||||||
day: 2 | month: 1 | year: 2006
|
day: 2 | month: 1 | year: 2006
|
||||||
```
|
```
|
||||||
|
|
||||||
### Applying multiple formats
|
### Applying multiple formats
|
||||||
|
Every `format` process can only apply a single pattern. Use `-k` to keep unmatched lines so the next `format` instance can apply another input pattern to them
|
||||||
Every format process can only apply a single pattern. Use `-k` to keep unmatched lines so the next format instance can apply another input pattern to them
|
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
```
|
```txt
|
||||||
2022-04-18
|
2022-04-18
|
||||||
1970-01-01
|
1970-01-01
|
||||||
02.01.2006
|
02.01.2006
|
||||||
@ -180,11 +215,12 @@ Input:
|
|||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
format -i '(\d{4})-(\d{2})-(\d{2})' -o 'day: {3:%d} | month: {2:%d} | year: {1}' -k | format -i '(\d{2})\.(\d{2})\.(\d{4})' -o 'day: {1:%d} | month: {2:%d} | year: {3}' -k
|
format -i '(\d{4})-(\d{2})-(\d{2})' -o 'day: {3::%d} | month: {2::%d} | year: {1}' -k |
|
||||||
|
format -i '(\d{2})\.(\d{2})\.(\d{4})' -o 'day: {1::%d} | month: {2::%d} | year: {3}' -k
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
day: 18 | month: 4 | year: 2022
|
day: 18 | month: 4 | year: 2022
|
||||||
day: 1 | month: 1 | year: 1970
|
day: 1 | month: 1 | year: 1970
|
||||||
day: 2 | month: 1 | year: 2006
|
day: 2 | month: 1 | year: 2006
|
||||||
@ -192,9 +228,10 @@ day: 2 | month: 2 | year: 1962
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Parsing multi-line patterns
|
### Parsing multi-line patterns
|
||||||
|
Use `-n` to change the amount of lines fed into the input pattern
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
```
|
```txt
|
||||||
year: 2022
|
year: 2022
|
||||||
month: 04
|
month: 04
|
||||||
day: 18
|
day: 18
|
||||||
@ -211,11 +248,11 @@ day: 02
|
|||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
format -n 3 -i '^year: (\d{4})\nmonth: (\d{2})\nday: (\d{2})$' -o 'day: {3:%d} | month: {2:%d} | year: {1}'
|
format -n 3 -i '^year: (\d{4})\nmonth: (\d{2})\nday: (\d{2})$' -o 'day: {3::%d} | month: {2::%d} | year: {1}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
day: 18 | month: 4 | year: 2022
|
day: 18 | month: 4 | year: 2022
|
||||||
day: 1 | month: 1 | year: 1970
|
day: 1 | month: 1 | year: 1970
|
||||||
day: 2 | month: 1 | year: 2006
|
day: 2 | month: 1 | year: 2006
|
||||||
@ -223,9 +260,10 @@ day: 2 | month: 2 | year: 1962
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Adding 2 values together
|
### Adding 2 values together
|
||||||
|
Use mutators to apply simple arithmetic on
|
||||||
|
|
||||||
Input:
|
Input:
|
||||||
```
|
```txt
|
||||||
5 7
|
5 7
|
||||||
3 2
|
3 2
|
||||||
10 152
|
10 152
|
||||||
@ -234,11 +272,11 @@ Input:
|
|||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
format -i '(-?\d+) (-?\d+(?:.\d+)?)' -o '{1} + {2} = {1:%g:+(2)}'
|
format -i '(-?\d+) (-?\d+(?:.\d+)?)' -o '{1} + {2} = {1::%g:+(2)}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
5 + 7 = 12
|
5 + 7 = 12
|
||||||
3 + 2 = 5
|
3 + 2 = 5
|
||||||
10 + 152 = 162
|
10 + 152 = 162
|
||||||
@ -247,10 +285,10 @@ Output:
|
|||||||
|
|
||||||
### Bulk renaming files
|
### Bulk renaming files
|
||||||
|
|
||||||
Rename a bunch of files at once using format
|
Rename a bunch of files at once using `format`
|
||||||
|
|
||||||
Output of `ls`:
|
Output of `ls`:
|
||||||
```
|
```txt
|
||||||
000.jpg
|
000.jpg
|
||||||
001.jpg
|
001.jpg
|
||||||
002.jpg
|
002.jpg
|
||||||
@ -258,11 +296,11 @@ Output of `ls`:
|
|||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
ls | format -i '(\d+)\.jpg' -o 'mv "{0}" "{1:%d:+1}.jpg"' | xargs -0 sh -c
|
ls | format -i '(\d+)\.jpg' -o 'mv "{0}" "{1::%d:+1}.jpg"' | xargs -0 sh -c
|
||||||
```
|
```
|
||||||
|
|
||||||
Output of `ls` afterwards:
|
Output of `ls` afterwards:
|
||||||
```
|
```txt
|
||||||
1.jpg
|
1.jpg
|
||||||
2.jpg
|
2.jpg
|
||||||
3.jpg
|
3.jpg
|
||||||
@ -275,7 +313,7 @@ Content:
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
if [ "$3" = "exec" ]; then
|
if [ "$3" = "exec" ]; then
|
||||||
command ls | format -i "$1" -o "mv \"{0}\" \"$2\"" | xargs -0 -P 4 sh -c
|
command ls | format -0 -i "$1" -o "mv \"{0}\" \"$2\"" | xargs -0 -P 4 -I {} sh -c "{}"
|
||||||
else
|
else
|
||||||
command ls | format -i "$1" -o "mv \"{0}\" \"$2\""
|
command ls | format -i "$1" -o "mv \"{0}\" \"$2\""
|
||||||
echo
|
echo
|
||||||
@ -291,7 +329,7 @@ There are a few things to consider using this script:
|
|||||||
Example usage of this script:
|
Example usage of this script:
|
||||||
|
|
||||||
Output of `ls`:
|
Output of `ls`:
|
||||||
```
|
```txt
|
||||||
000.jpg
|
000.jpg
|
||||||
001.jpg
|
001.jpg
|
||||||
002.jpg
|
002.jpg
|
||||||
@ -299,25 +337,25 @@ Output of `ls`:
|
|||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
bulkrename '(\d+)\.jpg' '{1:%d:+1}.jpg'
|
bulkrename '(\d+)\.jpg' '{1::%d:+1}.jpg'
|
||||||
```
|
```
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
```
|
```txt
|
||||||
mv "000.jpg" "1.jpg"
|
mv "000.jpg" "1.jpg"
|
||||||
mv "001.jpg" "2.jpg"
|
mv "001.jpg" "2.jpg"
|
||||||
mv "002.jpg" "3.jpg"
|
mv "002.jpg" "3.jpg"
|
||||||
|
|
||||||
execute commands with 'bulkrename (\d+)\.jpg {1:%d:+1}.jpg exec'
|
execute commands with 'bulkrename (\d+)\.jpg {1::%d:+1}.jpg exec'
|
||||||
```
|
```
|
||||||
|
|
||||||
Command:
|
Command:
|
||||||
```sh
|
```sh
|
||||||
bulkrename '(\d+)\.jpg' '{1:%d:+1}.jpg' exec
|
bulkrename '(\d+)\.jpg' '{1::%d:+1}.jpg' exec
|
||||||
```
|
```
|
||||||
|
|
||||||
Output of `ls` afterwards:
|
Output of `ls` afterwards:
|
||||||
```
|
```txt
|
||||||
1.jpg
|
1.jpg
|
||||||
2.jpg
|
2.jpg
|
||||||
3.jpg
|
3.jpg
|
||||||
|
8
go.mod
8
go.mod
@ -1,9 +1,13 @@
|
|||||||
module git.tordarus.net/Tordarus/format
|
module git.milar.in/milarin/format
|
||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.13.0 // indirect
|
git.milar.in/milarin/buildinfo v1.0.0
|
||||||
|
github.com/fatih/color v1.13.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
|
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -1,3 +1,5 @@
|
|||||||
|
git.milar.in/milarin/buildinfo v1.0.0 h1:tw98GupUYl/0a/3aPGuezhE4wseycOSsbcLp70hy60U=
|
||||||
|
git.milar.in/milarin/buildinfo v1.0.0/go.mod h1:arI9ZoENOgcZcanv25k9y4dKDUhPp0buJrlVerGruas=
|
||||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
|
127
main.go
127
main.go
@ -11,14 +11,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"git.milar.in/milarin/buildinfo"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var ( //flags
|
||||||
DefaultOutput = "{0}"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// regex with sub groups
|
// regex with sub groups
|
||||||
input = flag.String("i", `^(.|\n)*?$`, "input pattern")
|
input = flag.String("i", `^(.|\n)*?$`, "input pattern")
|
||||||
|
|
||||||
@ -40,7 +36,7 @@ var (
|
|||||||
//
|
//
|
||||||
// The following number mutators (integers and floats) are allowed:
|
// The following number mutators (integers and floats) are allowed:
|
||||||
// + - * / ^ %
|
// + - * / ^ %
|
||||||
output = flag.String("o", DefaultOutput, "output pattern")
|
output = flag.String("o", "{0}", "output pattern")
|
||||||
|
|
||||||
// don't ignore lines which do not match against input.
|
// don't ignore lines which do not match against input.
|
||||||
// they will be copied without any changes
|
// they will be copied without any changes
|
||||||
@ -51,78 +47,48 @@ var (
|
|||||||
// it may be useful to have a boolean flag for this behavior
|
// it may be useful to have a boolean flag for this behavior
|
||||||
lineParseAmount = flag.Int("n", 1, "amount of lines to feed into input pattern")
|
lineParseAmount = flag.Int("n", 1, "amount of lines to feed into input pattern")
|
||||||
|
|
||||||
replacePattern = regexp.MustCompile(`\{(\d+)(?::(.*?))?(?::(.*?))?(?::(.*?))?\}`)
|
showVersion = flag.Bool("v", false, "show version and exit")
|
||||||
|
|
||||||
|
OutputNullByte = flag.Bool("0", false, "use nullbyte instead of newline as line separator for printing output")
|
||||||
|
)
|
||||||
|
|
||||||
|
var ( // globals
|
||||||
|
LineSeparator string = "\n"
|
||||||
|
|
||||||
|
replacePattern = regexp.MustCompile(`\{(\d+)(?::(.*?))?(?::(.*?))?(?::(.*?))?\}`)
|
||||||
numMutationPattern = regexp.MustCompile(`([+\-*/])(\d+|\((\d+)\))`)
|
numMutationPattern = regexp.MustCompile(`([+\-*/])(\d+|\((\d+)\))`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
if *showVersion {
|
||||||
|
buildinfo.Print(buildinfo.Options{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if *OutputNullByte {
|
||||||
|
LineSeparator = string(rune(0))
|
||||||
|
}
|
||||||
|
|
||||||
pattern, err := regexp.Compile(*input)
|
pattern, err := regexp.Compile(*input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reformatting := strings.HasPrefix(*input, "^") && strings.HasSuffix(*input, "$") && *output != DefaultOutput
|
escapedOutput := EscSeqReplacer.Replace(*output)
|
||||||
|
|
||||||
if reformatting {
|
|
||||||
escapedOutput := EscSeqReplacer.Replace(*output)
|
|
||||||
reformatLine(pattern, escapedOutput)
|
|
||||||
} else {
|
|
||||||
colorCodeMatches(pattern)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// reformatLine is using input pattern to replace
|
|
||||||
// placeholders in output pattern with the given subgroups
|
|
||||||
func reformatLine(pattern *regexp.Regexp, output string) {
|
|
||||||
for line := range readLines(os.Stdin) {
|
for line := range readLines(os.Stdin) {
|
||||||
matches := pattern.FindStringSubmatch(line)
|
matches := pattern.FindStringSubmatch(line)
|
||||||
if len(matches) == 0 {
|
|
||||||
if *keepUnmatched {
|
|
||||||
fmt.Println(line)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Println(replaceVars(output, matches...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// colorCodeMatches is using input pattern
|
|
||||||
// and color-codes all matches within input
|
|
||||||
func colorCodeMatches(pattern *regexp.Regexp) {
|
|
||||||
c := color.New(color.FgRed, color.Bold)
|
|
||||||
for line := range readLines(os.Stdin) {
|
|
||||||
matches := pattern.FindAllStringIndex(line, -1)
|
|
||||||
|
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
if *keepUnmatched {
|
if *keepUnmatched {
|
||||||
fmt.Println(line)
|
fmt.Print(line, LineSeparator)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
runes := []rune(line)
|
fmt.Print(replaceVars(escapedOutput, matches...), LineSeparator)
|
||||||
b := new(strings.Builder)
|
|
||||||
nextMatch := 0
|
|
||||||
currentIndex := 0
|
|
||||||
for currentRune := 0; currentRune < len(runes); currentRune++ {
|
|
||||||
if nextMatch >= len(matches) || currentIndex < matches[nextMatch][0] {
|
|
||||||
// handle next rune
|
|
||||||
b.WriteRune(runes[currentRune])
|
|
||||||
currentIndex += utf8.RuneLen(runes[currentRune])
|
|
||||||
} else {
|
|
||||||
// handle next match
|
|
||||||
match := line[matches[nextMatch][0]:matches[nextMatch][1]]
|
|
||||||
b.WriteString(c.Sprint(match))
|
|
||||||
currentIndex += len(match)
|
|
||||||
currentRune += utf8.RuneCountInString(match) - 1
|
|
||||||
nextMatch++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(b.String())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,32 +97,15 @@ func readLines(r io.Reader) <-chan string {
|
|||||||
|
|
||||||
go func(out chan<- string, source io.Reader) {
|
go func(out chan<- string, source io.Reader) {
|
||||||
defer close(out)
|
defer close(out)
|
||||||
|
|
||||||
r := bufio.NewReader(source)
|
r := bufio.NewReader(source)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var line string
|
line, err := ReadLine(r)
|
||||||
var err error
|
|
||||||
lines := make([]string, 0, *lineParseAmount)
|
|
||||||
for line, err = r.ReadString('\n'); ; line, err = r.ReadString('\n') {
|
|
||||||
if rn, size := utf8.DecodeLastRuneInString(line); rn == '\n' {
|
|
||||||
line = line[:len(line)-size]
|
|
||||||
}
|
|
||||||
|
|
||||||
lines = append(lines, line)
|
|
||||||
|
|
||||||
// stop reading as soon as lineParseAmount is reached or an error occured (most likely EOF)
|
|
||||||
if len(lines) == cap(lines) || err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
linesCombined := strings.Join(lines, "\n")
|
|
||||||
|
|
||||||
// use data as line if reading was successfull or EOF has been reached
|
// use data as line if reading was successfull or EOF has been reached
|
||||||
// in the latter case: only use data if something could be read until EOF
|
// in the latter case: only use data if something could be read until EOF
|
||||||
if err == nil || err == io.EOF && linesCombined != "" {
|
if err == nil || err == io.EOF && line != "" {
|
||||||
out <- linesCombined
|
out <- line
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -168,8 +117,30 @@ func readLines(r io.Reader) <-chan string {
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReadLine(r *bufio.Reader) (string, error) {
|
||||||
|
lines := make([]string, 0, *lineParseAmount)
|
||||||
|
|
||||||
|
var line string
|
||||||
|
var err error
|
||||||
|
for line, err = r.ReadString('\n'); ; line, err = r.ReadString('\n') {
|
||||||
|
if rn, size := utf8.DecodeLastRuneInString(line); rn == '\n' {
|
||||||
|
line = line[:len(line)-size]
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = append(lines, line)
|
||||||
|
|
||||||
|
// stop reading as soon as lineParseAmount is reached or an error occured (most likely EOF)
|
||||||
|
if len(lines) == cap(lines) || err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
linesCombined := strings.Join(lines, "\n")
|
||||||
|
return linesCombined, err
|
||||||
|
}
|
||||||
|
|
||||||
func replaceVars(format string, vars ...string) string {
|
func replaceVars(format string, vars ...string) string {
|
||||||
replacements := replacePattern.FindAllStringSubmatch(format, -1)
|
replacements := replacePattern.FindAllStringSubmatch(format, -1) // TODO arguments do not change in outer loop (can be moved to main method)
|
||||||
|
|
||||||
for _, replacement := range replacements {
|
for _, replacement := range replacements {
|
||||||
rplStr := replacement[0]
|
rplStr := replacement[0]
|
||||||
|
Loading…
Reference in New Issue
Block a user