All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.Bash.kt Maven / Gradle / Ivy

There is a newer version: 0.0.87
Show newest version
@file:Suppress("unused", "ClassName")

package pl.mareklangiewicz.kommand

import kotlinx.coroutines.flow.*
import pl.mareklangiewicz.annotations.DelicateApi

@DelicateApi
fun bash(script: String, pause: Boolean = false, init: Bash.() -> Unit = {}) =
  Bash().apply {
    -BashOpt.Command
    +if (pause) "$script ; echo END.ENTER; read" else script
    init()
  }

@DelicateApi
fun Kommand.withBash(pause: Boolean = false, init: Bash.() -> Unit = {}) = bash(this, pause, init)

@DelicateApi
fun bash(kommand: Kommand, pause: Boolean = false, init: Bash.() -> Unit = {}) = bash(kommand.line(), pause, init)
// FIXME_someday: I assumed kommand.line() is correct script and will not interfere with surrounding stuff

fun bashQuoteMetaChars(script: String) = script.replace(Regex("([|&;<>() \\\\\"\\t\\n])"), "\\\\$1")

@OptIn(DelicateApi::class)
fun bashEchoEnv(envName: String) = bash("echo \"$$envName\"")

@OptIn(DelicateApi::class)
fun bashGetExportsMap() =
  bash("export").reducedOut {
    this
      .toList()
      .mapNotNull { line -> Regex("declare -x (\\w+)=\"(.*)\"").matchEntire(line) }
      .associate { match -> match.groups[1]!!.value to match.groups[2]!!.value }
  }

@OptIn(DelicateApi::class)
fun bashGetExportsToFile(outFile: String) =
  bash("export > $outFile").reducedOutToUnit()


// TODO_someday: better bash composition support; make sure I correctly 'quote' stuff when composing Kommands with Bash
// https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Quoting
// TODO_maybe: typesafe DSL for composing bash scripts? (similar to URE)
// TODO NOW: mark all lowlevel stuff as delicate api; default api should always fail fast, for example:
//  - always check weird filenames (like with \n)
//  - bash -c more than one nonopt (is confusing and should be opt in)
//  - ssh host command separatearg (instead of ssh host "command arg") is delicate
//    because it always concatenate separatearg with just space and send to remote shell as one script
//  - generally all direct manipulation of Kommand classes should be marked as @DelicateApi!
@DelicateApi
data class Bash(
  override val opts: MutableList = mutableListOf(),
  /** Normally just one command string (with or without spaces) or a file (when no -c option provided) */
  override val nonopts: MutableList = mutableListOf(),
) : KommandTypical {
  override val name get() = "bash"
}

@DelicateApi
interface BashOpt : KOptTypical {
  /**
   * interpret first from nonopts as a command_string to run
   * If more nonopts present, they are used to override env variables $0 $1 $2...
   */
  data object Command : BashOpt, KOptS("c")
  data object Interactive : BashOpt, KOptS("i")
  data object Login : BashOpt, KOptS("l")
  data object Restricted : BashOpt, KOptS("r")

  /**
   * if the -s option is present, or if no arguments remain after option processing,
   * then commands are read from the standard input.
   * This option allows the positional parameters to be set
   * when invoking an interactiveshell or when reading input through a pipe.
   */
  data object Stdin : BashOpt, KOptS("s")
  data object Posix : BashOpt, KOptL("posix")
  data object Help : BashOpt, KOptL("help")
  data object Version : BashOpt, KOptL("version")

  /** Print shell input lines as they are read. */
  data object PrintInput : BashOpt, KOptS("v")

  /** Print commands and their arguments as they are executed. */
  data object PrintExecs : BashOpt, KOptS("x")
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy