Peios Learn
Products
PePeios pkpekit PvProvium UDUniversal Directory TrTrail PrProject WiWispist
Using Peios Security Basics Technical Documentation Source
Using Peios Security Basics Technical Documentation Source
Peios

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

CommandPurpose
echoPrint its arguments as a line of text.
printfPrint text from a format string, with precise control over layout.
yesPrint a line over and over, without stopping.
seqPrint a sequence of numbers.
teeCopy standard input to standard output and to each named file.

The environment

CommandPurpose
envRun a command with a modified environment — or print the current one.
printenvPrint environment variables.

Numbers

CommandPurpose
numfmtConvert numbers to and from human-readable forms (1.5G, 1000000).
factorPrint the prime factors of a number.

Evaluating

CommandPurpose
exprEvaluate an arithmetic, string, or comparison expression.
testEvaluate a condition — a file check or a comparison — as a true/false result.

Exit status

CommandPurpose
true and falseDo 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 #

OptionEffect
-nDo not print the trailing newline.
-eInterpret backslash escape sequences in the arguments (see below).
-EDo not interpret escape sequences. This is the default.

Escape sequences #

With -e, echo recognises these backslash sequences and prints the character they stand for:

SequenceCharacter
\\A literal backslash.
\nNewline.
\tHorizontal tab.
\rCarriage return.
\bBackspace.
\fForm feed.
\vVertical tab.
\aAlert (the terminal bell).
\eEscape.
\0NNNThe byte with octal value NNN.
\xHHThe byte with hexadecimal value HH.
\cStop — 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 #

CodeMeaning
0The output was written.
1The 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:

SequenceCharacter
\\Backslash.
\n \t \rNewline, tab, carriage return.
\b \f \v \aBackspace, form feed, vertical tab, alert.
\eEscape.
\NNNThe byte with octal value NNN.
\xHHThe byte with hexadecimal value HH.
\uHHHH, \UHHHHHHHHA Unicode character by hexadecimal code point.
%%A literal %.

Substitution fields #

A field begins with % and is replaced by the next argument, formatted accordingly.

FieldFormats the argument as…
%sa string.
%ba string, with backslash escapes in it interpreted.
%qa string, quoted so it is safe to reuse as shell input.
%ca single character.
%d, %ia signed integer.
%uan unsigned integer.
%x, %Xan unsigned integer in hexadecimal (lower- or upper-case).
%oan unsigned integer in octal.
%f, %Fa decimal floating-point number.
%e, %Ea number in scientific notation.
%g, %Gwhichever 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 #

CodeMeaning
0Success.
1A 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 #

CodeMeaning
0The output was closed normally.
1A 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 to last.
  • seq first last — count from first up to last.
  • seq first increment last — count from first to last, stepping by increment.

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 #

OptionEffect
-s, --separator=STRINGSeparate the numbers with STRING instead of a newline.
-t, --terminator=STRINGEnd the output with STRING instead of a newline.
-w, --equal-widthPad the numbers with leading zeros so they all have the same width.
-f, --format=FORMATFormat 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 #

CodeMeaning
0The sequence was printed.
1An 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 #

OptionEffect
-i, --ignore-environmentStart from an empty environment, not the inherited one — so the command sees only what you assign. A bare - argument means the same.
-u, --unset=NAMERemove NAME from the environment.
--file=FILERead 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 #

OptionEffect
-C, --chdir=DIRChange to DIR before running the command.
--argv0=NAMERun the command but present NAME as its zeroth argument.
-S, --split-string=SSplit 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:

OptionEffect
--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-handlingList the signal-handling changes the other options requested.

Other options #

OptionEffect
-0, --nullWhen printing the environment, end each line with a NUL character instead of a newline. Not valid together with a command.
-v, --debugPrint 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:

CodeMeaning
(command's own)The command ran; this is its exit status.
125env itself failed — for example, a bad option.
126The command was found but could not be run.
127The 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 #

OptionEffect
-0, --nullEnd 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 #

CodeMeaning
0Every named variable was found and printed.
1A named variable was not set.
2A 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 #

OptionEffect
--from=UNITParse input numbers that carry a UNIT suffix, scaling them up to plain digits.
--to=UNITScale output numbers down and add a UNIT suffix.

A UNIT is one of:

UnitSuffixes mean
noneNo suffixes — a suffix is an error. The default.
siPowers of 1000: 1K = 1000, 1M = 1000000.
iecPowers of 1024: 1K = 1024, 1M = 1048576.
iec-iPowers of 1024 with a two-letter suffix: 1Ki = 1024, 1Mi = 1048576.
autoAccept 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:

OptionEffect
--field=FIELDSConvert only the numbers in these fields. FIELDS uses the same range syntax as cut — 3, 2-5, 2-.
-d, --delimiter=XUse X to separate fields instead of whitespace.
--header[=N]Pass the first N lines through unconverted, as a header.

Shaping the output #

OptionEffect
--format=FORMATFormat the number with a printf-style floating-point FORMAT, controlling width, padding, and precision.
--padding=NPad the output to N columns — positive right-aligns, negative left-aligns.
--groupingGroup digits according to the locale (for example, 1,000,000).
--round=METHODChoose the rounding method used when scaling.
--suffix=SUFFIXAppend SUFFIX to each result, and accept it on input.
--from-unit=N / --to-unit=NSet the unit size the input or output is counted in.

Other options #

OptionEffect
--invalid=MODEChoose what to do with input that is not a valid number: abort, fail, warn, or ignore.
-z, --zero-terminatedTreat the NUL character as the line delimiter.
--debugPrint warnings about questionable input.

Exit status #

CodeMeaning
0Every number was converted.
2An 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 #

OperatorResult
ARG1 + ARG2Sum.
ARG1 - ARG2Difference.
ARG1 * ARG2Product.
ARG1 / ARG2Integer quotient.
ARG1 % ARG2Remainder.

Comparison #

A comparison yields 1 for true and 0 for false. It is arithmetic when both sides are numbers, and lexical otherwise.

OperatorTrue when…
ARG1 = ARG2the two are equal.
ARG1 != ARG2they are unequal.
ARG1 < ARG2ARG1 is less.
ARG1 <= ARG2ARG1 is less or equal.
ARG1 > ARG2ARG1 is greater.
ARG1 >= ARG2ARG1 is greater or equal.

Logic #

OperatorResult
ARG1 | ARG2ARG1 if it is neither null nor 0; otherwise ARG2.
ARG1 & ARG2ARG1 if neither argument is null or 0; otherwise 0.

String operations #

FormResult
length STRINGThe number of characters in STRING.
substr STRING POS LENGTHLENGTH characters of STRING, counting POS from 1.
index STRING CHARSThe position of the first of any of CHARS in STRING, or 0.
STRING : REGEXPMatch REGEXP anchored at the start of STRING. Returns the matched length, or the text captured by \(…\).
match STRING REGEXPThe 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:

CodeMeaning
0The result was neither null nor 0.
1The result was null or 0.
2The expression was syntactically invalid.
3An 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 #

OptionEffect
-h, --exponentsWrite a repeated factor in exponent form — p^e — instead of repeating it.
$ factor -h 360
360: 2^3 3^2 5

Exit status #

CodeMeaning
0Every number was factored.
1An 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 #

TestTrue when the file…
-e FILEexists.
-f FILEexists and is a regular file.
-d FILEexists and is a directory.
-L FILE, -h FILEexists and is a symbolic link.
-p FILEexists and is a named pipe (FIFO).
-S FILEexists and is a socket.
-b FILEexists and is a block device.
-c FILEexists and is a character device.
-s FILEexists and is not empty.

Access #

These three tests ask whether you may actually use the file:

TestTrue when…
-r FILEyou may read the file.
-w FILEyou may write the file.
-x FILEyou 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 #

TestTrue when…
FILE1 -nt FILE2FILE1 is newer than FILE2.
FILE1 -ot FILE2FILE1 is older than FILE2.
FILE1 -ef FILE2both name the same file (same device and inode).

Other file tests #

TestTrue when the file…
-t FDfile descriptor FD is open on a terminal.
-N FILEhas been modified since it was last read.
-O FILE, -G FILEcarries an owner-id / group-id field matching the caller's.
-g FILE, -u FILE, -k FILEhas 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 #

TestTrue when…
-n STRINGSTRING is not empty. A bare STRING means the same.
-z STRINGSTRING is empty.
STRING1 = STRING2the strings are equal.
STRING1 != STRING2the strings are unequal.
STRING1 < STRING2STRING1 sorts before STRING2.
STRING1 > STRING2STRING1 sorts after STRING2.

Integer comparisons #

TestTrue when…
A -eq BA equals B.
A -ne BA does not equal B.
A -lt BA is less than B.
A -le BA is less than or equal to B.
A -gt BA is greater than B.
A -ge BA is greater than or equal to B.

Combining conditions #

FormResult
! EXPRESSIONThe negation.
( EXPRESSION )Grouping — escape the parentheses so the shell does not take them.
EXPR1 -a EXPR2True when both are true.
EXPR1 -o EXPR2True 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 #

CodeMeaning
0The expression was true.
1The expression was false.
2The 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 true is the standard way to write a loop that runs until something inside it breaks out.
  • A deliberate placeholder. Setting a configurable command to true makes that step do nothing and "succeed"; false makes 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, true and false provide it.

They are the simplest commands there are — and the overview's note on exit status is the whole idea behind them.

Exit status #

CodeMeaning
0Returned by true, always.
1Returned 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:

ModeBehaviour
warnWarn on a write error to any output, including a broken pipe, and continue with the remaining outputs.
warn-nopipeWarn 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.
exitExit on a write error to any output, including a broken pipe.
exit-nopipeExit on a write error that is not a pipe error. Broken-pipe errors are ignored.

Options #

OptionEffect
-a, --appendAppend to each named file rather than truncating it.
-i, --ignore-interruptsIgnore the interrupt signal.
-pIgnore 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 #

CodeMeaning
0The input was copied to standard output and every named file without error.
1A file could not be opened, a write failed under a policy that reports or exits on it, or the input could not be read.

Peios Learn — documentation for the Peios project.

Built with Trail.