ЁЯПл The SchoolтА║ЁЯз╣ Shell scriptingтА║ЁЯП╖я╕П рдзрдбрд╛ 05 тАФ Variables рдЖрдгрд┐ quoting: name tags
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯП╖я╕П рдзрдбрд╛ 05 тАФ Variables рдЖрдгрд┐ quoting: name tags

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 05 ┬╖ рдкреБрдвреЗ: lesson-06-conditions-loops


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ05. рдЧреЛрд╖реНрдЯреАрдВрдирд╛ рдирд╛рд╡ рджреЗрдгреЗ, рддреЗ рдкрд░рдд рд╡рд╛рдЪрдгреЗ, рдЖрдгрд┐ рдЪрд╛рд▓рдгрд╛рд▒реНрдпрд╛ scripts рдирд╛ рдзреЛрдХрд╛рджрд╛рдпрдХ scripts рдкрд╛рд╕реВрди рд╡реЗрдЧрд│реЗ рдХрд░рдгрд╛рд░рд╛ quoting рдЪрд╛ рдирд┐рдпрдо.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

variable рдореНрд╣рдгрдЬреЗ рдПрдХрд╛ value рд╡рд░рдЪрд╛ name tag: REGISTER=data/register.csv. $REGISTER рдореНрд╣рдЯрд▓реЗ рдХреА рдХреЗрдЕрд░рдЯреЗрдХрд░ tag рд╡рд╛рдЪрддреЛ. рдкрдг рдХреЗрдЕрд░рдЯреЗрдХрд░ рд╢рдмреНрджрд╢рдГ рдРрдХрддреЛ: value рдордзреНрдпреЗ space рдЕрд╕реЗрд▓ рдЖрдгрд┐ рддреБрдореНрд╣реА рддрд┐рдЪреНрдпрд╛рднреЛрд╡рддреА quotes рдШрд╛рддрд▓реЗ рдирд╕рддреАрд▓, рддрд░ рддреНрдпрд╛рд▓рд╛ рджреЛрди рд╢рдмреНрдж рдРрдХреВ рдпреЗрддрд╛рдд. "$name" рдиреЗрд╣рдореА рдПрдХрдЪ рдЧреЛрд╖реНрдЯ рдЕрд╕рддреЗ. $name рдореНрд╣рдгрдЬреЗ "spaces рд╡рд░ рддреЛрдбрд╛ рдЖрдгрд┐ рдХреЛрдгрддрд╛рд╣реА * expand рдХрд░рд╛". рдпрд╛ рдлрд░рдХрд╛рдиреЗ рдХреЛрдгрддреНрдпрд╛рд╣реА virus рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд files delete рдХреЗрд▓реНрдпрд╛ рдЖрд╣реЗрдд.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
  A["name='My Report'"] --> B["rm \"$name\"  тЖТ rm 'My Report'  (1 argument)"]
  A --> C["rm $name  тЖТ rm My Report  (2 arguments, both wrong)"]

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

DIR unset рдЕрд╕рддрд╛рдирд╛ rm -rf $DIR/ expand рд╣реЛрдКрди rm -rf / рд╣реЛрддреЛ. ${DIR:?} рдЪрд╛рд▓рд╛рдпрд▓рд╛ рдирдХрд╛рд░ рджреЗрддреЛ; "$DIR" space рдЕрд╕рд▓реЗрд▓рд╛ path рдЕрдЦрдВрдб рдареЗрд╡рддреЛ; set -u (рдзрдбрд╛ 08) typo рдкрдХрдбрддреЛ. Quoting рд╣реА рд╢реИрд▓реА рдирд╛рд╣реА; рддреА рд╕реБрд░рдХреНрд╖рд┐рддрддрд╛ рдЖрд╣реЗ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

school.sh рдордзрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ variable quoted рдЖрд╣реЗ. REGISTER рд▓рд╛ рдПрдХ default рдЖрд╣реЗ рдЬреЛ environment рдмрджрд▓реВ рд╢рдХрддреЗ; enrol ${1:?тАж} рдиреЗ рдЖрдкрд▓реЗ arguments рдорд╛рдЧрддреЛ; valid_class [[ " $CLASSES " == *" $1 "* ]] рдиреЗ рд╕рджрд╕реНрдпрддреНрд╡ рддрдкрд╛рд╕рддреЛ тАФ quotes рдЪреНрдпрд╛ рдЖрддрд▓реНрдпрд╛ spaces рдореБрджреНрджрд╛рдо рдареЗрд╡рд▓реЗрд▓реНрдпрд╛ рдЖрд╣реЗрдд.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

name="Zoya Khan"; printf '[%s]\n' $name; printf '[%s]\n' "$name"     # 2 arguments vs 1
SCHOOL_REGISTER=/tmp/reg.csv bin/school.sh list; echo "exit $?"       # the default, overridden тАФ and missing
cp data/register.csv /tmp/reg.csv && SCHOOL_REGISTER=/tmp/reg.csv bin/school.sh list --class 3B
bin/school.sh enrol; echo "exit $?"                                    # ${1:?} refuses
n=$(bin/school.sh list 2>/dev/null | tail -n +2 | wc -l); echo "students: $((n))"

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

[Zoya] [Khan] рдЖрдгрд┐ рдордЧ [Zoya Khan]; рдирд╕рд▓реЗрд▓реА рдиреЛрдВрджрд╡рд╣реА рд╕реНрдкрд╖реНрдЯ message рд╕рд╣ 1 рдиреЗ exit рд╣реЛрддреЗ; copy рдХреЗрд▓реЗрд▓реА рдиреЛрдВрджрд╡рд╣реА 3B рджрд╛рдЦрд╡рддреЗ; arguments рд╢рд┐рд╡рд╛рдп enrol enrol needs NAME рдЫрд╛рдкрддреЛ рдЖрдгрд┐ 1 рдиреЗ exit рд╣реЛрддреЛ; students: 5.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рддреБрдореНрд╣реА value рд╕реБрд░рдХреНрд╖рд┐рддрдкрдгреЗ рд╡рд╛рдЪреВ рд╢рдХрддрд╛, рддрд┐рд▓рд╛ default рджреЗрдК рд╢рдХрддрд╛, рддреА рдорд╛рдЧреВ рд╢рдХрддрд╛, рдЖрдгрд┐ рдПрдХрд╛ рдареЛрд╕ input рдиреЗ quotes рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рд╣реЗ рд╕рдордЬрд╛рд╡реВ рд╢рдХрддрд╛.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: deploy script рдордзрд▓реНрдпрд╛ space рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдПрдХрд╛ unquoted path рдиреЗ рдПрдХрджрд╛ Steam for Linux рд╡рд░ /usr рдкреБрд╕реВрди рдЯрд╛рдХрд▓реЗ. рдЙрдкрд╛рдп рд╣реЛрддрд╛ quotes рдЪреА рдПрдХ рдЬреЛрдбреА рдЖрдгрд┐ ${VAR:?}.

тПня╕П рдкреБрдвреЗ

рдзрдбрд╛ 06 тАФ conditions рдЖрдгрд┐ loops: рдХреЗрдЕрд░рдЯреЗрдХрд░ exit codes рд╡рд░реВрди рдирд┐рд░реНрдгрдп рдШреЗрддреЛ.

ЁЯП╖я╕П Lesson 05 тАФ Variables & quoting: name tags

ЁЯУН You are here: Lesson 05 of 12 ┬╖ Next: lesson-06-conditions-loops


ЁЯУж What's in this branch

Lessons 01тАУ05. Naming things, reading them back, and the quoting rule that separates working scripts from dangerous ones.

ЁЯзТ Explain like I'm 5

A variable is a name tag on a value: REGISTER=data/register.csv. Say $REGISTER and the caretaker reads the tag. But the caretaker is literal: if the value has a space in it and you did not put quotes around it, they hear two words. "$name" is one thing, always. $name is "split it on spaces and expand any *". That difference has deleted more files than any virus.

ЁЯЧ║я╕П Diagram

flowchart LR
  A["name='My Report'"] --> B["rm \"$name\"  тЖТ rm 'My Report'  (1 argument)"]
  A --> C["rm $name  тЖТ rm My Report  (2 arguments, both wrong)"]

тЭУ What

ЁЯдФ Why

rm -rf $DIR/ with DIR unset expands to rm -rf /. ${DIR:?} refuses to run; "$DIR" keeps a space-containing path whole; set -u (lesson 08) catches the typo. Quoting is not style; it is safety.

ЁЯФз How (in this repo)

Every variable in school.sh is quoted. REGISTER has a default the environment can override; enrol demands its arguments with ${1:?тАж}; valid_class tests membership with [[ " $CLASSES " == *" $1 "* ]] тАФ spaces inside the quotes on purpose.

ЁЯзк Try it

name="Zoya Khan"; printf '[%s]\n' $name; printf '[%s]\n' "$name"     # 2 arguments vs 1
SCHOOL_REGISTER=/tmp/reg.csv bin/school.sh list; echo "exit $?"       # the default, overridden тАФ and missing
cp data/register.csv /tmp/reg.csv && SCHOOL_REGISTER=/tmp/reg.csv bin/school.sh list --class 3B
bin/school.sh enrol; echo "exit $?"                                    # ${1:?} refuses
n=$(bin/school.sh list 2>/dev/null | tail -n +2 | wc -l); echo "students: $((n))"

тЬЕ Verify тАФ what you should see

[Zoya] [Khan] then [Zoya Khan]; the missing register exits 1 with a clear message; the copied one lists 3B; enrol without arguments prints enrol needs NAME and exits 1; students: 5.

ЁЯПБ What you just proved

You can read a value safely, give it a default, demand it, and explain with a concrete input why the quotes matter.

тЪая╕П Common mistakes

ЁЯПн Why this matters in production: an unquoted path with a space in a deploy script once wiped /usr on Steam for Linux. The fix was one pair of quotes and ${VAR:?}.

тПня╕П Next

Lesson 06 тАФ conditions & loops: the caretaker decides with exit codes.

тЖР Previoustext toolsNext тЖТconditions loops

This page is the lesson's README from the lesson-05-variables-quoting branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.