Output and evaluation
Single-page view · as markdown
Output and evaluation
Peios / Using Peios / Peiosutils / Output and evaluation
This topic gathers the small, sharp commands that scripts are built from: the ones that print something, that read or set the environment, that evaluate an expression or a condition, and that simply succeed or fail.
The commands #
Producing text
| Command | Purpose |
|---|---|
echo | Print its arguments as a line of text. |
printf | Print text from a format string, with precise control over layout. |
yes | Print a line over and over, without stopping. |
seq | Print a sequence of numbers. |
tee | Copy standard input to standard output and to each named file. |
The environment
| Command | Purpose |
|---|---|
env | Run a command with a modified environment — or print the current one. |
printenv | Print environment variables. |
Numbers
| Command | Purpose |
|---|---|
numfmt | Convert numbers to and from human-readable forms (1.5G, 1000000). |
factor | Print the prime factors of a number. |
Evaluating
| Command | Purpose |
|---|---|
expr | Evaluate an arithmetic, string, or comparison expression. |
test | Evaluate a condition — a file check or a comparison — as a true/false result. |
Exit status
| Command | Purpose |
|---|---|
true and false | Do nothing, and succeed — or do nothing, and fail. |
A note on exit status #
Several commands here exist to be used for their exit status rather than their output. Every command, when it finishes, leaves behind a number: 0 means success, anything else means a kind of failure. A shell's if, &&, and || all act on that number.
test is the clearest case — it prints nothing and exists only to set an exit status from a condition. true and false are the most minimal: fixed success and fixed failure. Where this topic's pages give an exit-status table, that number is often the whole point of the command.
Where to start #
For everyday printing, echo; for anything that needs columns, padding, or precise formatting, printf.
echo
Peios / Using Peios / Peiosutils / Output and evaluation
echo prints its arguments, separated by single spaces, followed by a newline.
echo [options] [string...]
$ echo Hello, Peios
Hello, Peios
It is the simplest way to put a line of text on standard output — printing a message, or feeding a fixed string into a pipe.
Options #
| Option | Effect |
|---|---|
-n | Do not print the trailing newline. |
-e | Interpret backslash escape sequences in the arguments (see below). |
-E | Do not interpret escape sequences. This is the default. |
Escape sequences #
With -e, echo recognises these backslash sequences and prints the character they stand for:
| Sequence | Character |
|---|---|
\\ | A literal backslash. |
\n | Newline. |
\t | Horizontal tab. |
\r | Carriage return. |
\b | Backspace. |
\f | Form feed. |
\v | Vertical tab. |
\a | Alert (the terminal bell). |
\e | Escape. |
\0NNN | The byte with octal value NNN. |
\xHH | The byte with hexadecimal value HH. |
\c | Stop — produce no further output. |
echo and printf #
echo is fixed: arguments, spaces, a newline. The moment you need control over the output — columns, padding, a number formatted a particular way, output with no spaces between pieces — use printf instead. printf is also more predictable across environments, since echo's handling of escapes and -n varies.
A note on shells #
Many command shells provide their own built-in echo, and the shell's version is what runs when you type echo at a prompt. Built-in versions vary — especially in how they treat -n, -e, and escape sequences — so their behaviour may differ from what is described here. To be certain you are running this command rather than the shell built-in, invoke it by its full path.
Exit status #
| Code | Meaning |
|---|---|
0 | The output was written. |
1 | The output could not be written. |
printf
Peios / Using Peios / Peiosutils / Output and evaluation
printf prints text built from a format string. The format string is printed literally, except that escape sequences become characters and substitution fields are replaced by the arguments that follow.
printf format [argument...]
$ printf 'the letter %X comes before %X\n' 10 11
the letter A comes before B
Where echo prints arguments as-is, printf gives you control: padding, column widths, number bases, fixed decimal places. It is the command for output that has to line up or be exact.
printf writes only what the format string says — there is no automatic trailing newline. Include \n yourself where you want one.
Escape sequences #
The format string interprets these backslash sequences:
| Sequence | Character |
|---|---|
\\ | Backslash. |
\n \t \r | Newline, tab, carriage return. |
\b \f \v \a | Backspace, form feed, vertical tab, alert. |
\e | Escape. |
\NNN | The byte with octal value NNN. |
\xHH | The byte with hexadecimal value HH. |
\uHHHH, \UHHHHHHHH | A Unicode character by hexadecimal code point. |
%% | A literal %. |
Substitution fields #
A field begins with % and is replaced by the next argument, formatted accordingly.
| Field | Formats the argument as… |
|---|---|
%s | a string. |
%b | a string, with backslash escapes in it interpreted. |
%q | a string, quoted so it is safe to reuse as shell input. |
%c | a single character. |
%d, %i | a signed integer. |
%u | an unsigned integer. |
%x, %X | an unsigned integer in hexadecimal (lower- or upper-case). |
%o | an unsigned integer in octal. |
%f, %F | a decimal floating-point number. |
%e, %E | a number in scientific notation. |
%g, %G | whichever of decimal or scientific notation is shorter. |
Width and precision #
Between the % and the field letter you may put two numbers — %[width][.precision]field:
- width — the minimum number of columns. Output narrower than this is padded with leading spaces; a negative width pads on the right instead.
- precision — after a
.; its meaning depends on the field. For a string it is a maximum length; for an integer, a minimum digit count (zero-padded); for a float, the number of decimal places.
$ printf '%-10s %5.2f\n' apples 3.1
apples 3.10
Reusing the format string #
If more arguments are supplied than the format string has fields, printf repeats the format string until the arguments run out. If there are too few, the leftover fields default to an empty string (or 0 for a number).
$ printf '%s: %d\n' alpha 1 beta 2 gamma 3
alpha: 1
beta: 2
gamma: 3
Exit status #
| Code | Meaning |
|---|---|
0 | Success. |
1 | A missing operand, a bad format string, or a write failure. |
yes
Peios / Using Peios / Peiosutils / Output and evaluation
yes prints a line over and over, without stopping.
yes [string...]
$ yes
y
y
y
...
With no argument it prints y forever. Given arguments, it prints them — joined by spaces — as the repeated line instead.
$ yes retry
retry
retry
...
yes never stops on its own. It ends when something downstream closes the pipe, or when you interrupt it with Ctrl-C.
What it is for #
yes exists to answer prompts. An older interactive command that asks "are you sure? [y/n]" again and again can be fed a stream of confirmations:
$ yes | slow-interactive-command
Most modern commands have a proper option for this — rm -f, and similar — and that is the better way when it exists. yes is the fallback for a command that offers no such option. It is also a quick way to generate an endless stream of identical lines for a test.
Exit status #
| Code | Meaning |
|---|---|
0 | The output was closed normally. |
1 | A write error occurred. |
seq
Peios / Using Peios / Peiosutils / Output and evaluation
seq prints a sequence of numbers, one per line.
seq [options] last
seq [options] first last
seq [options] first increment last
$ seq 3
1
2
3
$ seq 2 2 10
2
4
6
8
10
The three forms control where the sequence starts and how it steps:
seq last— count from 1 up tolast.seq first last— count fromfirstup tolast.seq first increment last— count fromfirsttolast, stepping byincrement.
The increment may be negative, to count down, and the numbers may be fractional. seq is most often used to drive a loop a fixed number of times.
Options #
| Option | Effect |
|---|---|
-s, --separator=STRING | Separate the numbers with STRING instead of a newline. |
-t, --terminator=STRING | End the output with STRING instead of a newline. |
-w, --equal-width | Pad the numbers with leading zeros so they all have the same width. |
-f, --format=FORMAT | Format each number with a printf-style floating-point FORMAT. |
$ seq -s , 1 5
1,2,3,4,5
$ seq -w 8 10
08
09
10
-f takes a single floating-point conversion — see printf for the format syntax — and cannot be combined with -w.
Exit status #
| Code | Meaning |
|---|---|
0 | The sequence was printed. |
1 | An argument was not a valid number, the increment was zero, or no argument was given. |
env
Peios / Using Peios / Peiosutils / Output and evaluation
env runs a command with a changed environment — the set of NAME=VALUE variables a process inherits. Run with no command, it prints the current environment instead.
env [options] [NAME=VALUE]... [command [arg]...]
$ env LANG=C TZ=UTC my-program
$ env
PATH=/bin
HOME=/home/jack
...
How it works #
You give env a list of NAME=VALUE assignments, then a command. env applies the assignments to its own environment and then runs the command, which inherits the result. The assignments affect only that one run — your shell's environment is untouched.
This is the clean way to run something with a particular variable set, without changing it for everything else, and the standard way to run a command with one variable temporarily different.
With no command, env simply prints the environment it would have used — which, with no options, is the current one.
Building the environment #
| Option | Effect |
|---|---|
-i, --ignore-environment | Start from an empty environment, not the inherited one — so the command sees only what you assign. A bare - argument means the same. |
-u, --unset=NAME | Remove NAME from the environment. |
--file=FILE | Read NAME=VALUE lines from a .env-style FILE and apply them. |
The order is: start (inherited, or empty with -i), apply --file, apply --unset, apply the NAME=VALUE arguments.
Running the command #
| Option | Effect |
|---|---|
-C, --chdir=DIR | Change to DIR before running the command. |
--argv0=NAME | Run the command but present NAME as its zeroth argument. |
-S, --split-string=S | Split S into separate arguments. This exists for script interpreter lines, where everything after the interpreter arrives as one string. |
Signal handling #
env can adjust how the command treats signals before launching it:
| Option | Effect |
|---|---|
--ignore-signal[=SIG] | Set the named signals to be ignored. |
--default-signal[=SIG] | Reset the named signals to their default handling. |
--block-signal[=SIG] | Block delivery of the named signals while the command runs. |
--list-signal-handling | List the signal-handling changes the other options requested. |
Other options #
| Option | Effect |
|---|---|
-0, --null | When printing the environment, end each line with a NUL character instead of a newline. Not valid together with a command. |
-v, --debug | Print each processing step env performs. |
Exit status #
When env runs a command, it exits with that command's status. The exception is a failure in env itself:
| Code | Meaning |
|---|---|
| (command's own) | The command ran; this is its exit status. |
125 | env itself failed — for example, a bad option. |
126 | The command was found but could not be run. |
127 | The command was not found. |
printenv
Peios / Using Peios / Peiosutils / Output and evaluation
printenv prints environment variables.
printenv [options] [variable...]
Name one or more variables and printenv prints the value of each. Name none and it prints the whole environment, one NAME=VALUE pair per line.
$ printenv HOME
/home/jack
$ printenv
PATH=/bin
HOME=/home/jack
...
Options #
| Option | Effect |
|---|---|
-0, --null | End each output line with a NUL character instead of a newline — so values that themselves contain newlines can still be told apart. |
printenv and env #
Both can print the environment. printenv is the one built for reading it — it can pick out a single variable by name. env prints the environment too, but its real job is running a command with a modified one. Use printenv to look something up; use env to change something for a command.
Exit status #
| Code | Meaning |
|---|---|
0 | Every named variable was found and printed. |
1 | A named variable was not set. |
2 | A usage error. |
numfmt
Peios / Using Peios / Peiosutils / Output and evaluation
numfmt converts numbers between plain digits and human-readable forms — turning 1500000 into 1.5M, or 1.5M back into 1500000.
numfmt [options] [number...]
$ numfmt --to=si 1500000
1.5M
$ numfmt --from=si 1.5M
1500000
numfmt takes numbers as arguments, or — with none — reads them from standard input, which lets it rescale a column of numbers flowing through a pipe.
The direction of conversion #
| Option | Effect |
|---|---|
--from=UNIT | Parse input numbers that carry a UNIT suffix, scaling them up to plain digits. |
--to=UNIT | Scale output numbers down and add a UNIT suffix. |
A UNIT is one of:
| Unit | Suffixes mean |
|---|---|
none | No suffixes — a suffix is an error. The default. |
si | Powers of 1000: 1K = 1000, 1M = 1000000. |
iec | Powers of 1024: 1K = 1024, 1M = 1048576. |
iec-i | Powers of 1024 with a two-letter suffix: 1Ki = 1024, 1Mi = 1048576. |
auto | Accept any of the above on input, reading Ki/Mi as powers of 1024 and bare K/M as powers of 1000. |
Working on fields #
By default numfmt converts the whole input line. To convert numbers sitting in a column of wider text, name the field:
| Option | Effect |
|---|---|
--field=FIELDS | Convert only the numbers in these fields. FIELDS uses the same range syntax as cut — 3, 2-5, 2-. |
-d, --delimiter=X | Use X to separate fields instead of whitespace. |
--header[=N] | Pass the first N lines through unconverted, as a header. |
Shaping the output #
| Option | Effect |
|---|---|
--format=FORMAT | Format the number with a printf-style floating-point FORMAT, controlling width, padding, and precision. |
--padding=N | Pad the output to N columns — positive right-aligns, negative left-aligns. |
--grouping | Group digits according to the locale (for example, 1,000,000). |
--round=METHOD | Choose the rounding method used when scaling. |
--suffix=SUFFIX | Append SUFFIX to each result, and accept it on input. |
--from-unit=N / --to-unit=N | Set the unit size the input or output is counted in. |
Other options #
| Option | Effect |
|---|---|
--invalid=MODE | Choose what to do with input that is not a valid number: abort, fail, warn, or ignore. |
-z, --zero-terminated | Treat the NUL character as the line delimiter. |
--debug | Print warnings about questionable input. |
Exit status #
| Code | Meaning |
|---|---|
0 | Every number was converted. |
2 | An input was not a valid number, or an option was misused. |
expr
Peios / Using Peios / Peiosutils / Output and evaluation
expr evaluates a single expression and prints its value.
expr expression
$ expr 6 + 7
13
$ expr length "Peios"
5
Each part of the expression — every number, operator, and keyword — is a separate argument. That is why the spaces matter: expr 6+7 is one argument and not an expression, while expr 6 + 7 is three.
Arithmetic #
| Operator | Result |
|---|---|
ARG1 + ARG2 | Sum. |
ARG1 - ARG2 | Difference. |
ARG1 * ARG2 | Product. |
ARG1 / ARG2 | Integer quotient. |
ARG1 % ARG2 | Remainder. |
Comparison #
A comparison yields 1 for true and 0 for false. It is arithmetic when both sides are numbers, and lexical otherwise.
| Operator | True when… |
|---|---|
ARG1 = ARG2 | the two are equal. |
ARG1 != ARG2 | they are unequal. |
ARG1 < ARG2 | ARG1 is less. |
ARG1 <= ARG2 | ARG1 is less or equal. |
ARG1 > ARG2 | ARG1 is greater. |
ARG1 >= ARG2 | ARG1 is greater or equal. |
Logic #
| Operator | Result |
|---|---|
ARG1 | ARG2 | ARG1 if it is neither null nor 0; otherwise ARG2. |
ARG1 & ARG2 | ARG1 if neither argument is null or 0; otherwise 0. |
String operations #
| Form | Result |
|---|---|
length STRING | The number of characters in STRING. |
substr STRING POS LENGTH | LENGTH characters of STRING, counting POS from 1. |
index STRING CHARS | The position of the first of any of CHARS in STRING, or 0. |
STRING : REGEXP | Match REGEXP anchored at the start of STRING. Returns the matched length, or the text captured by \(…\). |
match STRING REGEXP | The same as STRING : REGEXP. |
Many of these operators — *, <, |, ( — are also meaningful to a shell, so quote or escape them so they reach expr intact.
Exit status #
expr's exit status reports on the value it computed:
| Code | Meaning |
|---|---|
0 | The result was neither null nor 0. |
1 | The result was null or 0. |
2 | The expression was syntactically invalid. |
3 | An error occurred during evaluation — such as division by zero. |
factor
Peios / Using Peios / Peiosutils / Output and evaluation
factor prints the prime factors of a number.
factor [options] [number...]
$ factor 360
360: 2 2 2 3 3 5
Each line of output is the original number, a colon, and its prime factors in increasing order, repeated as often as they divide in. Give several numbers and factor factors each; give none and it reads numbers from standard input.
Options #
| Option | Effect |
|---|---|
-h, --exponents | Write a repeated factor in exponent form — p^e — instead of repeating it. |
$ factor -h 360
360: 2^3 3^2 5
Exit status #
| Code | Meaning |
|---|---|
0 | Every number was factored. |
1 | An input was not a valid positive integer. |
test
Peios / Using Peios / Peiosutils / Output and evaluation
test evaluates a single condition and reports whether it is true or false. It prints nothing — the answer is its exit status: 0 for true, 1 for false. That makes it the command a shell's if, while, &&, and || are built on.
test expression
[ expression ]
The two forms are the same command. [ is test under another name; when invoked as [, it requires a closing ] as its last argument. [ -f notes.txt ] and test -f notes.txt are identical.
$ test -f notes.txt && echo "the file exists"
File tests #
Existence and type #
| Test | True when the file… |
|---|---|
-e FILE | exists. |
-f FILE | exists and is a regular file. |
-d FILE | exists and is a directory. |
-L FILE, -h FILE | exists and is a symbolic link. |
-p FILE | exists and is a named pipe (FIFO). |
-S FILE | exists and is a socket. |
-b FILE | exists and is a block device. |
-c FILE | exists and is a character device. |
-s FILE | exists and is not empty. |
Access #
These three tests ask whether you may actually use the file:
| Test | True when… |
|---|---|
-r FILE | you may read the file. |
-w FILE | you may write the file. |
-x FILE | you may execute the file, or search the directory. |
On Peios these are real access checks. test asks the kernel whether your token is granted the right in question — a live check against the file's security descriptor, the same decision any actual read, write, or execute would face. The answer is therefore honest: test -w FILE is true exactly when a write would be permitted.
-x on a regular file means "you are permitted to execute it" — which is a different question from whether the file is an executable. A file can be runnable code and still fail -x for you, and the two are decided separately; see Access decisions.
Comparing two files #
| Test | True when… |
|---|---|
FILE1 -nt FILE2 | FILE1 is newer than FILE2. |
FILE1 -ot FILE2 | FILE1 is older than FILE2. |
FILE1 -ef FILE2 | both name the same file (same device and inode). |
Other file tests #
| Test | True when the file… |
|---|---|
-t FD | file descriptor FD is open on a terminal. |
-N FILE | has been modified since it was last read. |
-O FILE, -G FILE | carries an owner-id / group-id field matching the caller's. |
-g FILE, -u FILE, -k FILE | has its set-group-id, set-user-id, or sticky inode bit set. |
The last two rows probe decorative inode fields. The Peios access model does not consult them — they are stored on the inode and reported for completeness, but they carry no authority. For a true picture of what may be done to a file, use the access tests -r, -w, -x above, not -O, -g, or -u.
String tests #
| Test | True when… |
|---|---|
-n STRING | STRING is not empty. A bare STRING means the same. |
-z STRING | STRING is empty. |
STRING1 = STRING2 | the strings are equal. |
STRING1 != STRING2 | the strings are unequal. |
STRING1 < STRING2 | STRING1 sorts before STRING2. |
STRING1 > STRING2 | STRING1 sorts after STRING2. |
Integer comparisons #
| Test | True when… |
|---|---|
A -eq B | A equals B. |
A -ne B | A does not equal B. |
A -lt B | A is less than B. |
A -le B | A is less than or equal to B. |
A -gt B | A is greater than B. |
A -ge B | A is greater than or equal to B. |
Combining conditions #
| Form | Result |
|---|---|
! EXPRESSION | The negation. |
( EXPRESSION ) | Grouping — escape the parentheses so the shell does not take them. |
EXPR1 -a EXPR2 | True when both are true. |
EXPR1 -o EXPR2 | True when either is true. |
-a and -o are genuinely ambiguous to parse and are best avoided. Combine separate test calls with the shell's own && and || instead:
test -f notes.txt && test -r notes.txt
A note on shells #
Many command shells provide their own built-in test and [, and the shell's version is what runs when you type test at a prompt or in a script. A built-in may not behave as described here — in particular, the access tests -r, -w, and -x are the real access checks described above only when this command runs. To be certain you are running this command rather than the shell built-in, invoke it by its full path.
Exit status #
| Code | Meaning |
|---|---|
0 | The expression was true. |
1 | The expression was false. |
2 | The expression was malformed. |
true and false
Peios / Using Peios / Peiosutils / Output and evaluation
true and false do nothing at all. Their only product is an exit status: true always succeeds, false always fails.
true
false
$ true ; echo $?
0
$ false ; echo $?
1
true exits 0. false exits 1. They take no arguments that matter, produce no output, and have no effect on anything.
What they are for #
A command that is only an exit status is useful as a fixed value in places that expect a command:
- An always-true or always-false loop.
while trueis the standard way to write a loop that runs until something inside it breaks out. - A deliberate placeholder. Setting a configurable command to
truemakes that step do nothing and "succeed";falsemakes it a step that always fails. - A known result in a test. When a script or a condition needs a guaranteed pass or fail to check against,
trueandfalseprovide it.
They are the simplest commands there are — and the overview's note on exit status is the whole idea behind them.
Exit status #
| Code | Meaning |
|---|---|
0 | Returned by true, always. |
1 | Returned by false, always. |
tee
Peios / Using Peios / Peiosutils / Output and evaluation
tee reads standard input and writes it, unchanged, to standard output and to each file you name. It is the T-junction of a pipeline: the data keeps flowing down the pipe while a copy is also captured on disk.
tee [options] [file...]
$ echo saved | tee note.txt
saved
$ cat note.txt
saved
The name is the shape of the letter T — one stream in, the same stream out two ways. That makes tee the tool for the moment you want to both see (or keep piping) some data and keep it at the same time.
Writing to several places at once #
Every byte that arrives on standard input is written to standard output and to each named file. Name more than one file and each one receives a full, identical copy:
$ producer | tee a.log b.log c.log | consumer
Here producer's output reaches consumer down the pipe exactly as if tee were not there, and a.log, b.log, and c.log each end up holding the same complete copy of it. With no files named, tee copies input straight to standard output and nothing else — a plain pass-through.
tee does not buffer between reads: it writes each chunk out as it is read, so a file being written by tee fills up as the data flows, rather than all at once when the input ends.
Overwrite or append #
By default, each named file is truncated — opened fresh, so any existing contents are discarded before the new data is written. A file that does not exist is created.
With -a (--append), tee appends to each named file instead: existing contents are kept and the new data is added to the end. A file that does not exist is still created.
$ echo first | tee log.txt # log.txt now holds "first"
$ echo second | tee -a log.txt # log.txt now holds "first" then "second"
Creating a file inherits a security descriptor #
When tee creates a new output file — a named file that did not already exist — that file needs a security descriptor, and it inherits one from the directory it is created in. This is the same creation-inherits-a-descriptor rule that governs every file-creating tool on Peios; see Files and directories for the shared model.
With -a, when the named file already exists, tee opens that existing file to append and creates nothing, so no new descriptor is involved. Whether opening a file to write succeeds — whether creating a new one or writing an existing one — is decided by the normal access check against the relevant directory or file. tee reports a file it cannot open and, by default, carries on with the outputs it can write.
Files named - #
tee gives no special meaning to -. A file argument of - refers to a file literally named - in the current directory; it is opened, created, and written exactly like any other named file. It is not a stand-in for standard output — standard output is always written and needs no naming.
Ignoring interrupts #
With -i (--ignore-interrupts), tee ignores the interrupt signal (the one a terminal sends on Ctrl-C). This lets tee keep copying to its files even as an interrupt tears down the rest of a pipeline, so the capture is not cut short.
Behaviour on write errors #
By default, if a write to one output fails, tee reports it and stops writing to that output, but keeps writing to the others; a broken-pipe error on standard output is treated quietly. Two options change this policy.
-p sets write-error behaviour so that errors on a pipe (a downstream reader that has gone away) are ignored while other write errors are still reported.
--output-error[=MODE] sets the policy explicitly. Given on its own, with no =MODE, it selects warn-nopipe. The modes are:
| Mode | Behaviour |
|---|---|
warn | Warn on a write error to any output, including a broken pipe, and continue with the remaining outputs. |
warn-nopipe | Warn on a write error that is not a pipe error, and continue. Broken-pipe errors are ignored. This is the mode selected by a bare --output-error. |
exit | Exit on a write error to any output, including a broken pipe. |
exit-nopipe | Exit on a write error that is not a pipe error. Broken-pipe errors are ignored. |
Options #
| Option | Effect |
|---|---|
-a, --append | Append to each named file rather than truncating it. |
-i, --ignore-interrupts | Ignore the interrupt signal. |
-p | Ignore errors from writing to a broken pipe, still reporting other write errors. |
--output-error[=MODE] | Set the write-error policy: warn, warn-nopipe, exit, or exit-nopipe. A bare --output-error means warn-nopipe. |
Exit status #
| Code | Meaning |
|---|---|
0 | The input was copied to standard output and every named file without error. |
1 | A file could not be opened, a write failed under a policy that reports or exits on it, or the input could not be read. |