|
| 1 | +--- |
| 2 | +title: Calling Shell Commands from YAML |
| 3 | +date: 2025-07-21 |
| 4 | +draft: false |
| 5 | +authors: [ingydotnet] |
| 6 | +categories: [Summer-of-YS] |
| 7 | +edit: blog/2025-07-21.md |
| 8 | +comments: true |
| 9 | +--- |
| 10 | + |
| 11 | +Wouldn't it be handy to get data from shell commands in YAML? |
| 12 | + |
| 13 | +Well you can, and it's really easy. |
| 14 | + |
| 15 | +YS has a bunch of functions in its Standard Library for calling shell commands, |
| 16 | +and passing data to and from them. |
| 17 | + |
| 18 | +Let's take a look... |
| 19 | + |
| 20 | +<!-- more --> |
| 21 | + |
| 22 | + |
| 23 | +## The `shell` function |
| 24 | + |
| 25 | +When you just want to run a shell command and let its outputs print to the |
| 26 | +terminal, you can use the `shell` function. |
| 27 | + |
| 28 | +```bash |
| 29 | +$ ys - <<'...' |
| 30 | +!YS-v0 |
| 31 | +shell: 'fortune' |
| 32 | +... |
| 33 | +You have taken yourself too seriously. |
| 34 | +``` |
| 35 | + |
| 36 | +This is the same as running `fortune` in from the command line. |
| 37 | + |
| 38 | +You can also pass data to the shell command. |
| 39 | +Let's try the `tac` command, which reverses the lines of a file. |
| 40 | + |
| 41 | +```bash |
| 42 | +$ ys - <<'...' |
| 43 | +!YS-v0 |
| 44 | +shell {:in "foo\nbar\nbaz\n"}: 'tac' |
| 45 | +... |
| 46 | +baz |
| 47 | +bar |
| 48 | +foo |
| 49 | +``` |
| 50 | + |
| 51 | + |
| 52 | +## The `sh` function |
| 53 | + |
| 54 | +The `sh` function doesn't print the output to the terminal. |
| 55 | +It returns a mapping that contains a bunch of fields like the exit code, stdout, |
| 56 | +stderr, and more. |
| 57 | + |
| 58 | +```bash |
| 59 | +$ ys - <<'...' |
| 60 | +!YS-v0 |
| 61 | +result =: |
| 62 | + sh: 'echo "Hello, World!"' |
| 63 | +say: result.out |
| 64 | +... |
| 65 | +Hello, World! |
| 66 | +``` |
| 67 | + |
| 68 | +Let's look at the entire result object: |
| 69 | + |
| 70 | +```bash |
| 71 | +$ ys - <<'...' |
| 72 | +!YS-v0 |
| 73 | +result =: |
| 74 | + sh: 'echo "Hello, World!"' |
| 75 | +say: result |
| 76 | +... |
| 77 | +{:proc #object[java.lang.ProcessImpl 0x512d4b90 Process[pid=1364706, exitValue=0]], :exit 0, :in #object[java.lang.ProcessImpl$ProcessPipeOutputStream 0x22ebcf7e java.lang.ProcessImpl$ProcessPipeOutputStream@22ebcf7e], :out Hello, World! |
| 78 | +, :err , :prev nil, :cmd [echo Hello, World!]} |
| 79 | +``` |
| 80 | + |
| 81 | +That's kind of ugly and hard to read. |
| 82 | + |
| 83 | +Let's use the `yaml/dump` function to pretty print it: |
| 84 | + |
| 85 | +```bash |
| 86 | +$ ys - <<'...' |
| 87 | +!YS-v0 |
| 88 | +result =: |
| 89 | + sh: 'echo "Hello, World!"' |
| 90 | +say: result:yaml/dump |
| 91 | +... |
| 92 | +proc: !!java.lang.ProcessImpl {} |
| 93 | +exit: 0 |
| 94 | +in: !!java.lang.ProcessImpl$ProcessPipeOutputStream {} |
| 95 | +out: | |
| 96 | + Hello, World! |
| 97 | +err: '' |
| 98 | +prev: null |
| 99 | +cmd: |
| 100 | +- echo |
| 101 | +- Hello, World! |
| 102 | +``` |
| 103 | + |
| 104 | +Much better! |
| 105 | + |
| 106 | +You can also pass data to the `sh` function just like the `shell` function. |
| 107 | + |
| 108 | + |
| 109 | +## The `sh-out` function |
| 110 | + |
| 111 | +The `sh-out` function is like the `sh` function, but it returns the output as a |
| 112 | +string. |
| 113 | + |
| 114 | +```bash |
| 115 | +$ ys - <<'...' |
| 116 | +!YS-v0 |
| 117 | +say: |
| 118 | + sh-out: 'echo "Hello, World!"' |
| 119 | +... |
| 120 | +Hello, World! |
| 121 | +``` |
| 122 | + |
| 123 | +This handy shortcut is often just what the doctor ordered. |
| 124 | + |
| 125 | + |
| 126 | +## The `bash` function |
| 127 | + |
| 128 | +You can only run simple shell commands with the `shell` and `sh` functions. |
| 129 | + |
| 130 | +In other words, you can't do things like pipes and redirects. |
| 131 | + |
| 132 | +Of course you can also put any complicated shell command inside: |
| 133 | + |
| 134 | +``` |
| 135 | +bash -c 'complicated | shell > command' |
| 136 | +``` |
| 137 | + |
| 138 | +The `bash` function basically does this for you in YS code. |
| 139 | + |
| 140 | +```bash |
| 141 | +$ ys - <<'...' |
| 142 | +!YS-v0 |
| 143 | +say: |
| 144 | + get _ :out: |
| 145 | + bash: 'fortune | tr a-z A-Z > /tmp/fortune.txt && |
| 146 | + ls -l /tmp/fortune.txt && cat /tmp/fortune.txt' |
| 147 | +... |
| 148 | +-rw-r--r-- 1 ingy ingy 46 Jul 21 21:27 /tmp/fortune.txt |
| 149 | +LOOK AFAR AND SEE THE END FROM THE BEGINNING. |
| 150 | +``` |
| 151 | + |
| 152 | +## The Shell's the Limit |
| 153 | + |
| 154 | +As you can imagine, there's no limit to what you can do with shell commands in |
| 155 | +YAML using YS. |
| 156 | + |
| 157 | +Don't worry. |
| 158 | +Later we'll explore ways to actually limit capabilities in YS if you need to. |
0 commit comments