Skip to content

Commit

Permalink
StrToAge can be called with shorter specs, better help text, append $…
Browse files Browse the repository at this point in the history
… for more exact file matching, updated documentation
  • Loading branch information
andreas committed Mar 6, 2017
1 parent 44177c4 commit 3d1e927
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 8 deletions.
153 changes: 152 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,153 @@
# graft
graft is a tool to find and transfer files written in go
graft is a command line application written in go to search directories and transfer files.

It supports glob patterns or regular expressions as well as resuming partial transferred files, preserving file attributes and exporting and importing lists.

***graft*** started as a learning project and it still is, so much of the code could be vastly improved and may contain bugs,
but for now it already is a useful tool that works well in most cases.


## Installation

***graft*** should support Windows, MacOS and Linux, although the usage instructions might be different for each operating system. You can download the latest pre-compiled binary from the [release page](releases/latest) or if you already installed go development tools, install graft via:

```
go get github.com/sandreas/graft
```

## Usage

***graft*** internally uses a combination of globbing conversion and regular expressions for matching and replacing directory patterns.

### Basic Usage

Basic usage of graft is:

```
graft [options] source [destination]
```

### Search mode

The destination pattern is optional, as well as the other programm options. If you do not specify a destination pattern, ***graft*** recursively lists all matching files in all subdirectories, so it can also be used as a search tool.

### Copy, Rename, Resume

***graft*** copies recursively and resumes partially transferred files by default. If you would like to move / rename files instead, use the ---move option


### Notes for Windows vs. Unix
Unix-Shells expand * and $1 by default, so use ***single quotes*** (') for all patterns to prevent unexpected results:

```
graft '/tmp/*.jpg'
```

On Windows, use ***double quotes*** (") and ***slashes*** (/) as directory separator:

```
graft "C:/Temp/*.jpg"
```

It usually is a good idea, to use the ***--dry-run*** option, to see, what graft is going to do with your files.


### Simple Examples

Recursive listing of all jpg files in /tmp directory using a simple glob pattern:

```
graft '/tmp/*.jpg'
```

Recursive copy every jpg file from tmp to /home/johndoe/pictures

```
graft '/tmp/*.jpeg' '/home/johndoe/pictures/$1.jpeg'
```

Recursive rename all files with extension jpeg to jpg:

```
graft '/tmp/*.jpeg' '/tmp/$1.jpg' --move
```

### Submatches and more complex examples

As a result of using regular expressions, you can use `()` in combination with `$` to create submatches, e.g.:

```
graft '/tmp/(*).(jpeg)' '/home/johndoe/pictures/$1_new.$2'
```

will copy following source files to their destination:

/tmp/test.jpeg => /home/johndoe/pictures/test_new.jpeg
/tmp/subdir/other.jpeg => /home/johndoe/pictures/subdir/other_new.jpeg


If you would like to match `()` in directorynames or filenames, they have to be escaped via backslash (\\):
```
graft '/tmp/videos \(2016\)' '/home/johndoe/'
```

You could also use braces to match groups of chars:
```
graft '/tmp/*.{jpg,png}' '/home/johndoe/$1'
```

#### Option reference

Following options are available:
```
Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
--export-to="" export source listing to file, one line per item
--files-from="" import source listing from file, one line per item
--min-age="" minimum age (e.g. 2d, 8w, 2016-12-24, etc. - see docs for valid time formats)
--max-age="" maximum age (e.g. 2d, 8w, 2016-12-24, etc. - see docs for valid time formats)
--case-sensitive be case sensitive when matching files and folders
--dry-run dry-run / simulation mode
--hide-matches hide matches in search mode ($1: ...)
--move move / rename files - do not make a copy
--quiet quiet mode - do not show any output
--regex use a real regex instead of glob patterns (e.g. src/.*\.jpg)
--times transfer source modify times to destination
Args:
<source-pattern> source pattern - used to locate files (e.g. src/*)
[<destination-pattern>] destination pattern for transfer (e.g. dst/$1)
```

The parameters --min-age and --max-age take duration or date strings to specify the age. Valid formats for age parameters, used like --min-age=X are:

```
1s => 1 second
2m => 2 minutes
3h => 3 hours
4d => 4 days
5w => 5 weeks
6mon => 6 months
7y => 7 years
2006-01-02 => exact date 2006-01-02 00:00:00
2006-01-02T15:04:05.000Z => exact date 2006-01-02 15:04:05
```

# development

***graft*** is developed go and uses the default go build command

```
git clone https://github.com/sandreas/graft.git
cd graft
go build
```

If the build is successful, the directory should contain a binary named `graft`

## IDE recommendation

***graft*** is developed with JetBrains IntelliJ IDEA, so this is the recommended IDE
11 changes: 8 additions & 3 deletions graft.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ var (
sourcePatternParameter = app.Arg("source-pattern", "source pattern - used to locate files (e.g. src/*)").Required().String()
destinationPatternParameter = app.Arg("destination-pattern", "destination pattern for transfer (e.g. dst/$1)").Default("").String()

exportTo = app.Flag("export-to", "export source listing to file, one line per found item").Default("").String()
exportTo = app.Flag("export-to", "export source listing to file, one line per item").Default("").String()
filesFrom = app.Flag("files-from", "import source listing from file, one line per item").Default("").String()

minAge = app.Flag("min-age", " minimum age (e.g. -2 days, -8 weeks, 2015-10-10, etc.)").Default("").String()
maxAge = app.Flag("max-age", "maximum age (e.g. 2 days, 8 weeks, 2015-10-10, etc.)").Default("").String()
minAge = app.Flag("min-age", " minimum age (e.g. 2d, 8w, 2016-12-24, etc. - see docs for valid time formats)").Default("").String()
maxAge = app.Flag("max-age", "maximum age (e.g. 2d, 8w, 2016-12-24, etc. - see docs for valid time formats)").Default("").String()


caseSensitive = app.Flag("case-sensitive", "be case sensitive when matching files and folders").Bool()
Expand Down Expand Up @@ -102,6 +102,11 @@ func main() {
caseInsensitiveQualifier = ""
}

// append $ for end of string
if (!strings.HasSuffix(pat, "$")) || strings.HasSuffix(pat, "\\$") {
pat += "$"
}

compiledPattern, err := pattern.CompileNormalizedPathPattern(patternPath, caseInsensitiveQualifier + pat)
if err == nil && compiledPattern.NumSubexp() == 0 && pat != "" {
compiledPattern, err = pattern.CompileNormalizedPathPattern(patternPath, caseInsensitiveQualifier + "(" + pat + ")")
Expand Down
8 changes: 4 additions & 4 deletions pattern/pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,16 @@ func StrToAge(t string, reference time.Time) (time.Time, error) {

unit := strings.ToLower(submatches[2])

if(strings.HasPrefix(unit, "day")) {
if(strings.HasPrefix(unit, "d")) {
return reference.AddDate(0, 0, modifier), nil
}
if(strings.HasPrefix(unit, "week")) {
if(strings.HasPrefix(unit, "w")) {
return reference.AddDate(0, 0, modifier * 7), nil
}
if(strings.HasPrefix(unit, "month")) {
if(strings.HasPrefix(unit, "mon")) {
return reference.AddDate(0, modifier, 0), nil
}
if(strings.HasPrefix(unit, "year")) {
if(strings.HasPrefix(unit, "y")) {
return reference.AddDate(modifier, 0, 0), nil
}

Expand Down

0 comments on commit 3d1e927

Please sign in to comment.