{"id":37024578,"url":"https://github.com/philwalk/pallet","last_synced_at":"2026-01-14T02:58:37.209Z","repository":{"id":189994986,"uuid":"681765372","full_name":"philwalk/pallet","owner":"philwalk","description":"Platform Independent Tooling","archived":false,"fork":false,"pushed_at":"2025-02-08T23:21:03.000Z","size":691,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-06T19:50:50.470Z","etag":null,"topics":["expressive","file-operations","method-chaining","portable","posix","scala","scripting"],"latest_commit_sha":null,"homepage":"https://github.com/philwalk/pallet","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/philwalk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-22T17:57:41.000Z","updated_at":"2025-02-08T23:21:07.000Z","dependencies_parsed_at":"2024-01-02T00:47:46.767Z","dependency_job_id":"6eb94632-48b7-4680-acc1-c99aeca3d5da","html_url":"https://github.com/philwalk/pallet","commit_stats":null,"previous_names":["philwalk/pallet"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/philwalk/pallet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philwalk%2Fpallet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philwalk%2Fpallet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philwalk%2Fpallet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philwalk%2Fpallet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/philwalk","download_url":"https://codeload.github.com/philwalk/pallet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philwalk%2Fpallet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28408799,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T01:52:23.358Z","status":"online","status_checked_at":"2026-01-14T02:00:06.678Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["expressive","file-operations","method-chaining","portable","posix","scala","scripting"],"created_at":"2026-01-14T02:58:36.506Z","updated_at":"2026-01-14T02:58:37.191Z","avatar_url":"https://github.com/philwalk.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pallet\n\n\n### Library for Cross-Platform Development\n\n![CI](https://github.com/philwalk/pallet/actions/workflows/scala.yml/badge.svg)\n\n\u003cimg alt=\"pallet image\" width=240 src=\"images/wooden-pallet.png\"\u003e\n\n\n* Provides expressive idioms for writing portable code.\n\n* Write one version of a script that runs in 90% or more of development environments.\n\nThe JVM on non-Windows platforms share similarities, and tend to be unix-like.\nAlthough bash shell environments exist, the `Windows` JVM doesn't support the posix\nfilesystem abstractions they provide.\n\nThis library leverages [Unifile](https://github.com/philwalk/unifile) to resolve these\ndeficiencies.  In addition, it provides Date \u0026 Time functions, `csv` support, and other\nenhancements.\n\n* Supported Scala Versions\n  * `scala 3.x`\n\n* Tested Target environments\n  * `Linux`\n  * `Darwin/OSX`\n  * `Windows`\n    * `Cygwin64`\n    * `Msys64`\n    * `Mingw64`\n    * `Git-bash`\n    * `WSL Linux`\n\n### Usage\n\nTo use this library in `scala-cli` or `scala 3.5+` scripts:\n```scala\n#!/usr/bin/env -S scala-cli shebang\n\n//\u003e using dep org.vastblue::unifile:0.4.1\n//\u003e using dep org.vastblue::pallet:0.11.0\n\nimport vastblue.pallet.*\n```\n\nFor sbt projects:\n```sbt\n\n  \"org.vastblue\" % \"pallet\"  %% \"0.11.0\" // scala 3\n  \"org.vastblue\" % \"pallet_3\" % \"0.11.0\" // scala 2.13.x\n```\n## Summary\n* Use `scala` instead of `bash` or `python` for portable general purpose scripting\n* Publish one script version, rather than multiple OS-customized versions\n* Script as though you're running in Linux, even on Windows or Mac.\n* standard OS and platform variables, based on `uname` information\n* directly read csv file rows from `java.nio.file.Path` objects\n* lots of commonly-needed file extensions:\n  * `if scriptPath.path.lastModified \u003e \"/etc/hosts\".path.lastModified then ...`\nExtends the range of scala scripting:\n* reference Windows filesystem paths via posix abstractions\n* predefined environment information:\n  * osType: String     \n  * osName: String     \n  * scalaHome: String\n  * javaHome: String \n  * userhome: String \n  * username: String \n  * hostname: String \n  * uname: String\n  * shellRoot: String \n  * isLinux: Boolean   \n  * isWinshell: Boolean\n  * isDarwin: Boolean  \n  * isWsl: Boolean     \n  * isCygwin: Boolean \n  * isMsys: Boolean   \n  * isMingw: Boolean  \n  * isGitSdk: Boolean \n  * isGitbash: Boolean\n  * isWindows: Boolean\n  * verbose: Boolean \n\n* extension methods on `java.nio.file.Path` and `java.io.File`\n  * name: String\n  * basename: String // drop extension\n  * parent: Path\n  * lines: Iterator[String]\n  * md5: String\n  * sha256: String\n  * cksum: Long\n  * lastModified: Long\n  * newerThan(other: Path): Boolean\n  * olderThan(other: Path): Boolean\n  * lastModifiedMillisAgo: Long\n  * lastModSecondsAgo: Double\n  * lastModMinutesAgo: Double\n  * lastModHoursAgo: Double\n  * lastModDaysAgo: Double\n  * withFileWriter(p: Path)(func: PrintWriter =\u003e Any)\n* iterate directory subfiles:\n  * files: Iterator[JFile]\n  * paths: Iterator[Path]\n  * walkTree(file: JFile, depth: Int = 1, maxdepth: Int = -1): Iterable[JFile]\n\n* read files in the `/proc` tree in Windows, e.g.:\n  * `/proc/meminfo`\n  * `/proc/$PID/cmdline`\n\n## Requirements\nIn Windows, requires installing a posix shell:\n  * [MSYS64](https://msys2.org)\n  * [CYGWIN64](https://www.cygwin.com)\n  * [WSL](https://learn.microsoft.com/en-us/windows/wsl/install)\n  * [Git Bash](https://gitforwindows.org/)\n\nIn `Darwin/OSX`, requires `homebrew` or similar.\n\nBest with a recent version of coreutils:\n  (e.g., `ubuntu`: 8.32-4.1ubuntu1, `osx`: stable 9.4)\n\n### Concept\n\n### Setup for running the example scripts:\nrecommended scala-cli `hash-bang` line:\n  * `#!/usr/bin/env -S scala-cli shebang`\n\n### Portable Conversion of Path Strings to java.nio.file.Path\nA posix shell path such as \"/etc/fstab\" is not recognized by the Windows jvm\nas referring to \"C:\\msys64\\etc\\fstab\", and attempting to read from it probably\nthrows `FileNotFoundException`.\n\nThe following command lines illustrate the default Windows JVM behavior:\n```scala\n# prints `true` to the Console for Windows paths:\nscala -e 'println(java.nio.file.Paths.get(\"C:/Windows\").toFile.isDirectory)'\n```\n```scala\n# prints `false` to the Console for mounted posix paths:\nscala -e 'println(java.nio.file.Paths.get(\"/etc/fstab\").toFile.isFile)'\n```\n\n### Example OS portable scripts\n\n#### display the native path and the number of lines in `/etc/fstab`\n```scala\n#!/usr/bin/env -S scala-cli shebang\n\n//\u003e using dep \"org.vastblue::pallet::0.11.0\"\n\nimport vastblue.pallet.*\nval p = Paths.get(\"/etc/fstab\")\nprintf(\"env: %-10s| posixroot: %-12s| %-22s| %d lines\\n\",\n  uname(\"-o\"), posixroot, p.posx, p.lines.size)\n```\n#### Output of the previous example scripts on various platforms:\n```\nLinux Mint # env: GNU/Linux | posixroot: /           | /etc/fstab            | 21 lines\nDarwin     # env: Darwin    | posixroot: /           | /etc/fstab            | 0 lines\nWSL Ubuntu # env: GNU/Linux | posixroot: /           | /etc/fstab            | 6 lines\nCygwin64   # env: Cygwin    | posixroot: C:/cygwin64 | C:/cygwin64/etc/fstab | 24 lines\nMsys64     # env: Msys      | posixroot: C:/msys64/  | C:/msys64/etc/fstab   | 22 lines\n```\nNote that on Darwin, there is no `/etc/fstab` file, so the `Path#lines` extension returns `Nil`.\n\n#### Example: list child directories of \".\"\n```scala\n#!/usr/bin/env -S scala-cli shebang\n\n//\u003e using dep \"org.vastblue::pallet::0.11.0\"\n\nimport vastblue.pallet.*\n\n// list child directories of \".\"\nval cwd: Path = Paths.get(\".\")\nfor ( p: Path \u003c- cwd.paths.filter { _.isDirectory }) {\n  printf(\"%s\\n\", p.posx)\n}\n```\n#### Example: print the native paths of command line arguments\n```scala\n#!/usr/bin/env -S scala-cli shebang\n\n//\u003e using dep \"org.vastblue::pallet::0.11.0\"\n\nimport vastblue.pallet.*\n\n// display native path of command-line provided filenames\nif args.isEmpty then\n  printf(\"usage: %s \u003cpath1\u003e [\u003cpath2\u003e ...]\\n\", scriptPath)\nelse\n  val dirs = for\n    fname \u003c- args\n    p = Paths.get(fname)\n    if p.isFile\n  yield p.posx\n\n  printf(\"%s\\n\", dirs.toList.mkString(\"\\n\"))\n```\n\n### Setup\n  * `Windows`: install one of the following:\n    * [MSYS64](https://msys2.org)\n    * [CYGWIN64](https://www.cygwin.com)\n    * [WSL](https://learn.microsoft.com/en-us/windows/wsl/install)\n    * [Git Bash](https://gitforwindows.org/)\n  * `Linux`: required packages:\n    * `sudo apt install coreutils`\n  * `Darwin/OSX`:\n    * `brew install coreutils`\n\n### How to Write Portable Scala Scripts\nThings that maximize the odds of your script running on most systems:\n  * use `scala 3`\n  * represent paths internally with forward slashes\n  * drive letter not needed for paths on the current working drive (often C:)\n    * to access disks other than the working drive, mount them via `/etc/fstab`\n    * `vastblue.Paths.get()` can parse both `posix` and `Windows` filesystem paths\n  * don't rely on `java.File.pathSeparator` for parsing path strings\n  * split strings to lines using `\"(\\r)?\\n\"` rather than JVM default line ending\n    * `split(\"\\n\")` can leave carriage-return debris\n    * `split(java.io.File.separator) fails or leaves debris if input string came from another OS\n  * split PATH-like environment variables using `java.io.File.pathSeparator`\n  * create `java.nio.file.Path` objects in either of two ways:\n    * `vastblue.file.Paths.get(\"/etc/fstab\")\n    * `\"/etc/fstab\".path       // guaranteed to use `vastblue.file.Paths.get()`\n### Examples\nExamples below illustrate some of the capabilities.\n\n```scala\n#!/usr/bin/env -S scala-cli shebang\n\n//\u003e using dep \"org.vastblue::pallet::0.11.0\"\n\nimport vastblue.pallet.*\n\n  printf(\"uname / osType / osName:\\n%s\\n\", s\"platform info: ${unameLong} / ${osType} / ${osName}\")\n  if (isLinux) {\n    // uname is \"Linux\"\n    printf(\"hello Linux\\n\")\n  } else if (isDarwin) {\n    // uname is \"Darwin*\"\n    printf(\"hello Mac\\n\")\n  } else if (isWinshell) {\n    // isWinshell: Boolean = isMsys | isCygwin | isMingw | isGitSdk | isGitbash\n    printf(\"hello %s\\n\", unameShort)\n  } else if (envOrEmpty(\"MSYSTEM\").nonEmpty) {\n    printf(\"hello %s\\n\", envOrEmpty(\"MSYSTEM\"))\n  } else {\n    assert(isWindows, s\"unknown environment: ${unameLong} / ${osType} / ${osName}\")\n    printf(\"hello Windows\\n\")\n  }\n```\n```scala\n#!/usr/bin/env -S scala-cli shebang\n\n//\u003e using dep \"org.vastblue::pallet::0.11.0\"\n\nimport vastblue.pallet.*\n\nimport vastblue.pallet.*\nimport vastblue.file.ProcfsPaths.cmdlines\n\nvar verbose = false\nfor (arg \u003c- args) {\n  arg match {\n  case \"-v\" =\u003e\n    verbose = true\n  }\n}\nif (isLinux || isWinshell) {\n  printf(\"script name: %s\\n\\n\", scriptName)\n  // find /proc/[0-9]+/cmdline files\n  for ((procfile, cmdline) \u003c- cmdlines) {\n    if (verbose || cmdline.contains(scriptName)) {\n      printf(\"%s\\n\", procfile)\n      printf(\"%s\\n\\n\", cmdline)\n    }\n  }\n} else {\n  printf(\"procfs filesystem not supported in os [%s]\\n\", osType)\n}\n```\n```bash\n$ jsrc/procCmdline.sc\n```\noutput when run from a Windows `Msys64` bash session:\n```scala\nscript name: jsrc/procCmdline.sc\n\n/proc/32314/cmdline\n'C:\\opt\\jdk\\bin\\java.exe' '-Dscala.home=C:/opt/scala' '-classpath' 'C:/opt/scala/lib/scala-library-2.13.10.jar;C:/opt/scala/lib/scala3-library_3-3.4.3.jar;C:/opt/scala/lib/scala-asm-9.5.0-scala-1.jar;C:/opt/scala/lib/compiler-interface-1.3.5.jar;C:/opt/scala/lib/scala3-interfaces-3.4.3.jar;C:/opt/scala/lib/scala3-compiler_3-3.4.3.jar;C:/opt/scala/lib/tasty-core_3-3.4.3.jar;C:/opt/scala/lib/scala3-staging_3-3.4.3.jar;C:/opt/scala/lib/scala3-tasty-inspector_3-3.4.3.jar;C:/opt/scala/lib/jline-reader-3.19.0.jar;C:/opt/scala/lib/jline-terminal-3.19.0.jar;C:/opt/scala/lib/jline-terminal-jna-3.19.0.jar;C:/opt/scala/lib/jna-5.3.1.jar;;' 'dotty.tools.MainGenericRunner' '-classpath' 'C:/opt/scala/lib/scala-library-2.13.10.jar;C:/opt/scala/lib/scala3-library_3-3.4.3.jar;C:/opt/scala/lib/scala-asm-9.5.0-scala-1.jar;C:/opt/scala/lib/compiler-interface-1.3.5.jar;C:/opt/scala/lib/scala3-interfaces-3.4.3.jar;C:/opt/scala/lib/scala3-compiler_3-3.4.3.jar;C:/opt/scala/lib/tasty-core_3-3.4.3.jar;C:/opt/scala/lib/scala3-staging_3-3.4.3.jar;C:/opt/scala/lib/scala3-tasty-inspector_3-3.4.3.jar;C:/opt/scala/lib/jline-reader-3.19.0.jar;C:/opt/scala/lib/jline-terminal-3.19.0.jar;C:/opt/scala/lib/jline-terminal-jna-3.19.0.jar;C:/opt/scala/lib/jna-5.3.1.jar;;' '-deprecation' '-cp' 'target/scala-3.4.3/classes' './procCmdline.sc'\n\n/proc/32274/cmdline\n'bash' '/c/opt/scala/bin/scala' '-deprecation' '-cp' 'target/scala-3.4.3/classes' './procCmdline.sc'\n```\nExample #2: write and read `.csv` files:\n```scala\n#!/usr/bin/env -S scala-cli shebang\n\n//\u003e using dep \"org.vastblue::pallet::0.11.0\"\n\nimport vastblue.pallet.*\n\nval testFiles = Seq(\"tabTest.csv\", \"commaTest.csv\")\nfor (filename \u003c- testFiles){\n  val testFile: Path = filename.toPath\n\n  if (!testFile.exists) {\n    // create tab-delimited and comma-delimited test files\n    val delim: String = if filename.startsWith(\"tab\") then \"\\t\" else \",\"\n    testFile.withWriter() { w =\u003e\n      w.printf(s\"1st${delim}2nd${delim}3rd\\n\")\n      w.printf(s\"A${delim}B${delim}C\\n\")\n    }\n  }\n\n  assert(testFile.isFile)\n  printf(\"\\n# filename: %s\\n\", testFile.posx)\n  // display file text lines\n  for ((line: String, i: Int) \u003c- testFile.lines.zipWithIndex){\n    printf(\"%d: %s\\n\", i, line)\n  }\n  // display file csv rows\n  for (row: Seq[String] \u003c- testFile.csvRows){\n    printf(\"%s\\n\", row.mkString(\"|\"))\n  }\n}\n```\n```bash\n$ time jsrc/csvWriteRead.sc\n```\nOutput:\n```bash\n# filename: C:/Users/username/workspace/pallet/tabTest.csv\n0: 1st  2nd     3rd\n1: A    B       C\n1st|2nd|3rd\nA|B|C\n\n# filename: C:/Users/username/workspace/pallet/commaTest.csv\n0: 1st,2nd,3rd\n1: A,B,C\n1st|2nd|3rd\nA|B|C\n\nreal    0m4.269s\nuser    0m0.135s\nsys     0m0.411s\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphilwalk%2Fpallet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphilwalk%2Fpallet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphilwalk%2Fpallet/lists"}