Skip to content

Input/Output#

Our scripts can take input in many ways. We can prompt the user for some information, we can take input via STDIN (standard input), we can take command line arguments, and more. We've already seen arguments being passed to a script in the form of $1 and beyond. I want to demonstrate using standard input instead.

After we've received and worked with an input, we can produce outputs. We've seen this too, but I want to demonstrate two other possible solutions that will come in handy later: writing to a file and writing to STDOUT (standard output) with JSON data.

Standard Input#

Check this out:

1
2
$ cat script_example_4.sh | wc -l
35

Note

You can actually just do wc -l <filename> too, but that's not using STDIN, so it doesn't suit our needs.

Here we've used the cat command to print the contents of script_example_4.sh out via STDOUT and then piped it (|) to the STDIN of wc -l. We got the result of 35, which tells us there are 35 lines of code in the script.

The wc -l command is using its STDIN to read the content and count it. We can do the same with out script. Let's change it again, but keep it simple this time around:

1
2
3
4
#!/bin/bash
hello() {  echo "Hello"; } # note the semi-colon is required
world() {  echo "world"; }
echo "$(hello), $(world)! My name is ${&0}"

And now we can run it like this:

1
2
$ echo "Michael" | bash script_example_5.sh
Hello, world! My name is Michael

But if you run it without providing STDIN, then the command sits and waits for you to enter something. That's because there's nothing in /dev/stdin and as such, it's waiting for something. All you have to do here is type your input and then press ++cntrl+d++ and it'll trigger the same result for you.

This isn't how you'd handle STDIN in a professional script, but it's a good demonstration of how you can use standard input to take information from the user.

Standard Output#

So far, we've been using echo to print messages to the terminal. That's certainly useful, but I want to demonstrate writing to a file, too. It's actually really simple.

Our script has, so far, been working with echo and printing out messages. It's often quite useful to write data to files instead. Let's update our (simplified) script:

1
2
3
4
#!/bin/bash
hello() {  echo "Hello"; } # note the semi-colon is required
world() {  echo "world"; }
echo "$(hello), $(world)! My name is ${&0}" > output.txt

And we get a new file!

1
2
3
$ echo "Michael" | bash script_example_5.sh
$ cat output.txt
Hello, world! My name is Michael

So, the output is now persisted (saved) to the disk. There's something to be wary of here, however. We used the > character to "redirect" the output into a file, but a > means "overwrite" what is already existing in the file. Here's what I mean:

1
2
3
4
5
6
7
$ echo "Michael" | bash script_example_5.sh
$ echo "Michael" | bash script_example_5.sh
$ echo "Michael" | bash script_example_5.sh
$ echo "Michael" | bash script_example_5.sh
$ echo "Michael" | bash script_example_5.sh
$ cat output.txt
Hello, world! My name is Michael

I ran my command five times in a row, yet the file still only contains one line of text. The entire contents of the file are being overwritten. What if we just want to add or "append" to the file? Well then we use >>:

1
2
3
4
#!/bin/bash
hello() {  echo "Hello"; } # note the semi-colon is required
world() {  echo "world"; }
echo "$(hello), $(world)! My name is ${&0}" >> output.txt

And now we get the lines repeated:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ echo "Michael" | bash script_example_5.sh
$ echo "Michael" | bash script_example_5.sh
$ echo "Michael" | bash script_example_5.sh
$ echo "Michael" | bash script_example_5.sh
$ cat output.txt
Hello, world! My name is Michael
Hello, world! My name is Michael
Hello, world! My name is Michael
Hello, world! My name is Michael
Hello, world! My name is Michael

I ran the command four times, but I have five lines because one line was already present from the previous run. The four new lines have been appended to the file.

When you're writing scripts, you'll have to decide which of these options you want.

JSON Output#

A common way of providing output to other problems, especially in 2022, is via JSON. You'll come across JSON a lot later on in your career, when you're working with Cloud based services. It stands for JavaScript Object Notation and it's used to represent data in both a human readable manner and in a way computers can easily parse.

Let's update our script to produce some JSON output to STDOUT:

1
2
3
4
#!/bin/bash
hello() {  echo "Hello"; } # note the semi-colon is required
world() {  echo "world"; }
echo "{\"message\": \"$(hello), $(world)! My name is $(cat /dev/stdin)\"}"

Note

You'll see we've had to do \" in certain places. This tells the Bash scripting "engine" that we want to escape the " character, and it shouldn't be considered part of the Bash script, but instead it should be just a simple " character in the string itself.

This is called character escaping. It's worth checking it out and being aware of it as an idea. You'll face times when you'll need to escape characters, but not too often.

So now we get JSON as our output:

1
2
$ echo "Mike" | bash script_example_6.sh
{"message": "Hello, world! My name is Mike"}

This can make cleaner and easier to read with a tool called jq. Let's install the tool inside of our Ubuntu VM:

1
$ sudo apt install jq

And then we'll use the jq tool, which stands for JSON Query, to make our JSON output pretty:

1
2
3
4
$ echo "Mike" | bash script_example_6.sh | jq .
{
  "message": "Hello, world! My name is Mike"
}

Nice, but what's this "message" thing about? In JSON documents there are "keys" used to access part of the JSON "document". Let's demonstrate this with a simple Python script that reads our JSON document and prints the message.

Note

Don't worry about this code right now. You'll learn Python very shortly.

1
2
3
4
import json
import sys
data = json.load(sys.stdin)
print(data['message'])

And if I run it, piping (|) the output of our Bash script into the Python script's STDIN, then it can read the JSON document and access the message key:

1
2
$ echo "Mike" | bash script_example_6.sh | python3 read_message.py
Hello, world! My name is Mike

Notice how I've simply replaced jq . with python3 read_message.py. So python3 executes the read_message.py script and passes the output from script_example_6.sh and its STDOUT into the STDIN of the read_message.py script.

In the Python script, you can see we're accessing the message key: print(data['message']). Easy!

And that's the basics of simple input and output from a script into other tools/software. It can, and does, get more complicated than this, of course, but these are good basics to understand for now.

You'll see these kinds of things being used a lot in scripts, so this is a good introduction for now.