TT Lab
Get started
Learn Learning paths Courses

Shell Scripting

Loops and Functions

Continue in TT Lab

Goal

You will learn loops and functions in a form you can actually use. In particular, you will make them not break in boundary situations such as "when no files match", "when an argument contains a space", and "when there are no arguments at all".

Why it matters

Shell functions do not return values. Values you compute go out through standard output, and success or failure is reported through the exit code. Only when you keep this convention does natural usage such as if is_number "$x" become possible. And do not parse ls for a file list; leave it to the shell's glob. $(ls *.log) breaks on file names that contain spaces, and when nothing matches it feeds an error message into the loop. Using a glob instead creates another trap, where the pattern string remains as it is when nothing matches, so you need the [ -f "$f" ] || continue line of defense.

Steps

  1. Create /root/bin/loop1.sh so that it prints 1 through 5, one per line.
  2. Create the directory /root/loops/logs and put 4 .log files and 2 .txt files in it. Then create /root/bin/count.sh <디렉터리> (the argument is a directory) so that it prints only the number of .log files directly under that directory. If given an empty directory, it must print 0.
  3. Put at least 3 lines into /root/loops/users.csv in the format 이름,역할 (name, role), and create /root/bin/readcsv.sh so that it reads that file and prints one line at a time in the format 이름(역할) (name followed by the role in parentheses). Run it without arguments.
  4. In /root/bin/lib.sh, define a slugify function. It converts the words to lowercase and joins them with hyphens. /root/bin/use.sh must use it by loading that file with source. use.sh Hello World -> hello-world.
  5. Create /root/bin/isnum.sh <문자열> (the argument is a string). If it consists only of digits, the exit code is 0; otherwise it is a nonzero code. It must print nothing. An empty string, a sign, and a decimal point are all not numbers.
  6. Create /root/bin/each.sh so that for each argument it receives, it prints one line in the format [인자] (the argument in square brackets). each.sh "a b" c must give two lines. If there are no arguments, it prints nothing.
  7. Create /root/bin/sum.sh so that it prints the sum of the numeric arguments it receives. If there are no arguments, print 0.
  8. Create /root/bin/summary.sh <디렉터리> (the argument is a directory). It prints the number of .log files in that directory and the total number of lines in the following two lines.
    • files=<개수> (the count)
    • total_lines=<합계> (the total)

Notes

Print 1 through 5

Create /root/bin/loop1.sh so that it prints 1 through 5, one per line.

You can use for x in 1 2 3 4 5 or the brace expansion {1..5}.

Count files with a glob

Create the directory /root/loops/logs and put 4 .log files and 2 .txt files in it. Then create /root/bin/count.sh <디렉터리> (the argument is a directory) so that it prints only the number of .log files directly under that directory. If given an empty directory, it must print 0.

If no files match, the pattern string remains as it is. Filter it out with [ -f "$f" ] || continue.

Read a CSV one line at a time

Put at least 3 lines into /root/loops/users.csv in the format 이름,역할 (name, role), and create /root/bin/readcsv.sh so that it reads that file and prints one line at a time in the format 이름(역할) (name followed by the role in parentheses). Run it without arguments.

Use the form while IFS=, read -r a b and redirect the input after done.

Split a function out into a library

In /root/bin/lib.sh, define a slugify function. It converts the words to lowercase and joins them with hyphens. /root/bin/use.sh must use it by loading that file with source. use.sh Hello World -> hello-world.

Define the function in lib.sh and load it in use.sh with . or source. You must not copy and paste it.

Judge with an exit code

Create /root/bin/isnum.sh <문자열> (the argument is a string). If it consists only of digits, the exit code is 0; otherwise it is a nonzero code. It must print nothing. An empty string, a sign, and a decimal point are all not numbers.

If you print the result of the judgment, you cannot use it in an if statement. Report it only with return 0 / return 1.

Safely iterate over arguments that contain spaces

Create /root/bin/each.sh so that for each argument it receives, it prints one line in the format [인자] (the argument in square brackets). each.sh "a b" c must give two lines. If there are no arguments, it prints nothing.

Check the difference between for x in "$@" and for x in $*. When there are no arguments, it must do nothing.

Sum variable arguments

Create /root/bin/sum.sh so that it prints the sum of the numeric arguments it receives. If there are no arguments, print 0.

Do not forget to initialize the accumulator variable to 0. With no arguments, 0 must come out.

Directory summary report

Create /root/bin/summary.sh <디렉터리> (the argument is a directory). It prints the number of .log files in that directory and the total number of lines in the following two lines.

Count the number of files and the total number of lines together. Even for an empty directory, 0/0 must come out.