Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bash/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ repositories {

dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
compile "com.xenomachina:kotlin-argparser:2.0.7"
// https://mvnrepository.com/artifact/junit/junit
testCompile group: 'junit', name: 'junit', version: '4.12'

Expand Down
3 changes: 2 additions & 1 deletion bash/gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#Tue Feb 12 13:01:42 MSK 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip
1 change: 1 addition & 0 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/Command.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ abstract class Command {
"pwd" -> Pwd()
"exit" -> Exit(env)
"cat" -> Cat(args)
"grep" -> Grep(args)
else -> OuterCommand(name, args)
}
}
Expand Down
70 changes: 70 additions & 0 deletions bash/src/main/kotlin/hse/nedikov/bash/logic/commands/Grep.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package hse.nedikov.bash.logic.commands

import com.xenomachina.argparser.ArgParser
import com.xenomachina.argparser.ShowHelpException
import com.xenomachina.argparser.default
import hse.nedikov.bash.logic.Command
import java.io.*
import java.lang.IllegalArgumentException
import java.util.ArrayList

/**
* grep command which prints all lines with pattern matched substring
*/
class Grep(private val arguments: ArrayList<String>) : Command() {
/**
* Takes lines for grep from input
*/
override fun execute(input: PipedReader, output: PipedWriter) {
val args = parseArguments(::PipedGrepArgs, output) ?: return
grep(input, output, args)
}

/**
* Takes lines for grep from file from arguments
*/
override fun execute(output: PipedWriter) {
val args = parseArguments(::GrepArgs, output) ?: return
grep(args.file, output, args)
}

private fun <T> parseArguments(constructor: (ArgParser) -> T, output: Writer): T? {
val arr = Array(arguments.size) { i -> arguments[i] }
try {
return ArgParser(arr).parseInto(constructor)
} catch (e: ShowHelpException) {
e.printUserMessage(output, "grep", 80)
}
return null;
}

private fun grep(input: Reader, output: PipedWriter, args: PipedGrepArgs) {
var linesToWrite = 0;
input.forEachLine {
val wordEnd = if (args.wordRegexp) "\\b" else ""
val matchPattern = "$wordEnd${args.pattern}$wordEnd"
val regex = if (args.ignoreCase) Regex(matchPattern, RegexOption.IGNORE_CASE) else Regex(matchPattern)
if (it.contains(regex)) {
output.write("$it\n")
linesToWrite = args.afterContext;
} else if (linesToWrite > 0) {
output.write("$it\n")
linesToWrite--
}
}
}
}

private open class PipedGrepArgs(parser: ArgParser) {
val ignoreCase by parser.flagging("-i", "--ignore-case", help = "ignore case distinctions")
val wordRegexp by parser.flagging("-w", "--word-regexp", help = "force PATTERN to match only whole words")
val afterContext by parser.storing("-A", "--after-context", argName = "NUM",
help = "print NUM lines of trailing context") { toInt() }
.default(0)
.addValidator { if (this.value < 0) throw IllegalArgumentException("NUM should be non negative") }
val pattern by parser.positional("PATTERN")
}

private class GrepArgs(parser: ArgParser) : PipedGrepArgs(parser) {
val file by parser.positional("FILE") { FileReader(this) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,57 @@ class CommandsTest {
assertEquals("1 3 19", stringFromReader(reader))
}

@Test
fun grepTest() {
val reader = Grep(list("lol")).execute(readerFromString(keklolString))
assertEquals("""
lol kek
kek lol
lolol lo
looolol
lol
""".trimIndent(), toLF(stringFromReader(reader)))
}

@Test
fun grepWordRegexpTest() {
val reader = Grep(list("lol", "-w")).execute(readerFromString(keklolString))
assertEquals("""
lol kek
kek lol
lol
""".trimIndent(), toLF(stringFromReader(reader)))
}

@Test
fun grepIgnoreCaseTest() {
val reader = Grep(list("LoL", "-i")).execute(readerFromString(keklolString))
assertEquals("""
lol kek
kek lol
lolol lo
looolol
kekek LOL
lol
""".trimIndent(), toLF(stringFromReader(reader)))
}

@Test
fun grepAfterContextTest() {
var reader = Grep(list("lol", "-A", "5")).execute(readerFromString(keklolString))
assertEquals(keklolString, toLF(stringFromReader(reader)))
reader = Grep(list("lol", "-A", "1")).execute(readerFromString(keklolString))
assertEquals("""
lol kek
kek lol
kek kek
lolol lo
looolol
kek
lol
""".trimIndent(), toLF(stringFromReader(reader)))
}

companion object {
fun readerFromString(string: String): PipedReader {
val reader = PipedReader()
Expand All @@ -69,5 +120,23 @@ class CommandsTest {
reader.readLines().forEach { joiner.add(it) }
return joiner.toString()
}

fun toLF(str: String) = str.replace("\r\n", "\n")


val keklolString = """
lol kek
kek lol
kek kek
ke
eeek
lolol lo
looolol
kek
kekek LOL
kekekekekek
kekekekekekekek
lol
""".trimIndent()
}
}