Tam by se dalo ušetřit znaků

Tam by se dalo ušetřit znaků

Tam by se dalo ušetřit znaků

Tam by se dalo ušetřit znaků

Tam by se dalo ušetřit znaků

Tam by se dalo ušetřit znaků

Tam by se dalo ušetřit znaků

Tam by se dalo ušetřit znaků

Tam by se dal ušetřit znaků

Tam by se da ušetřit znaků

Tam by se d ušetřit znaků

Tam by se  ušetřit znaků

Tam by se ušetřit znaků

Tam by s ušetřit znaků

Tam by š ušetřit znaků

Tam by šl ušetřit znaků

Tam by šlo ušetřit znaků

Tam by šlo ušetřit znaků

Tam by šlo ušetřit znaků

Lukáš Bařinka
Installfest 2022

Zapisovat příkazy v shellu
co nejúsporněji?

  • Záleží na tom?
  • Je to správná cesta?
  • Zajímá to vůbec někoho?
Tweet
Tweet Tweet
Tweet Tweet Tweet
Tweet Tweet Tweet Tweet

Obsah

  1. Shell a ti druzí
  2. Čitelnost, úspornost, bezpečnost
  3. Časté problémy a jejich řešení
  4. Úspornější zápisy (kde vzít a nekrást)

Shell a ti druzí

Speciální znaky

  • Různá prostředí, různé znaky, různé významy
    Shell?**.txt#%{3..7}
    RE..*\.txt$^${3,7}
  • Interpretace a její zabránění (quoting) Někdy jednosměrně, někdy obousměrně
    Shell \ "" '' $'' ${} eval
    RE \ [] \(\) \+ ...

Speciální znaky v shellu

Je vůbec nějaký ne-alfanumerický znak,
který nemá v shellu speciální význam?


							   30 40 50 60 70 80 90 100 110 120
							-- ---------------------------------
							0:    (  2  <  F  P  Z  d   n   x
							1:    )  3  =  G  Q  [  e   o   y
							2:    *  4  >  H  R  \  f   p   z
							3: !  +  5  ?  I  S  ]  g   q   {
							4: "  ,  6  @  J  T  ^  h   r   |
							5: #  -  7  A  K  U  _  i   s   }
							6: $  .  8  B  L  V  `  j   t   ~
							7: %  /  9  C  M  W  a  k   u  DEL
							8: &  0  :  D  N  X  b  l   v
							9: '  1  ;  E  O  Y  c  m   w
						

Různé kontexty — Různá interpretace

Hlavní účel shellu

  • Nastavení prostředí proměnné LC_COLLATE=cs_CZ.UTF-8 sort
    #!/bin/sh PHP_FCGI_MAX_REQUESTS=10000 export PHP_FCGI_MAX_REQUESTS exec /usr/local/bin/php-cgi
  • Spouštění programů konstrukce rm *.{txt,bak} 2>"$err"
  • Programovací jazyk skripty for file; do    rm -- "${file}" || exit done

Při interpretaci se text čte po slovech

  • Slova oddělují metaznaky Pokud nejsou "quoted"
  • | & ; ( ) < > space tab newline
p1<f1|p2>f2;p3&p4
p1 <f1 | p2 >f2 ; p3 & p4

Spouštění programů

IFS=:
ll $PATH
ls -l /bin:/usr/bin:/usr/local/bin
ls -l /bin /usr/bin /usr/local/bin
ls
-l
/bin
/usr/bin
/usr/local/bin

shell: exec…(program, parameters…)

program: int main(int argc, char *argv[]) { … }

Čitelnost, úspornost, bezpečnost

Něco za něco

  • $var
  • "$var"
  • "${var}"
  • "${var:=default}"

  • declare -A array=( key1 value1 key2 value2 … )
  • declare -A array=( [key1]=value1 [key2]=value2 … )

Kdy je potřeba použít ""

  • Zamezení rozdělení na slova
    f="file name"; cp "$f" /tmp

Kdy je potřeba použít ""

  • Zamezení rozdělení na slova
    f=file name; cp file name /tmp

Kdy je potřeba použít ""

  • Zamezení rozdělení na slova
    f=file name; cp file name /tmp
  • Zamezení globbingu
    grep "a.*b"

Kdy je potřeba použít ""

  • Zamezení rozdělení na slova
    f=file name; cp file name /tmp
  • Zamezení globbingu
    grep a.*b

Kdy je potřeba použít ""

  • Zamezení rozdělení na slova
    f=file name; cp file name /tmp
  • Zamezení globbingu
    grep a.*b
  • Zamezení substitucí v here-documentu
    cat >f <<"DOC"
    $s_\$$__
    DOC

Kdy je potřeba použít ""

  • Zamezení rozdělení na slova
    f=file name; cp file name /tmp
  • Zamezení globbingu
    grep a.*b
  • Zamezení substitucí v here-documentu
    cat >f <<DOC
    $s_\$$__
    DOC

Kdy jsou "" kontraproduktivní

  • Uvozovky kolem uvozovek
    echo "$text"

Kdy jsou "" kontraproduktivní

  • Uvozovky kolem uvozovek
    echo "Result: "$text""

Kdy jsou "" kontraproduktivní

  • Uvozovky kolem uvozovek
    echo Result: $text

Kdy jsou "" kontraproduktivní

  • Uvozovky kolem uvozovek
    echo "Result: \"$text\""

Kdy jsou "" kontraproduktivní

  • Uvozovky kolem uvozovek
    echo Result: "$text"

Kdy jsou "" kontraproduktivní

  • Uvozovky kolem uvozovek
    echo "Result: "$text""
  • Potřebuji jednotlivá slova nebo globbing
    IFS=:; find $PATH -name command

Kdy jsou "" kontraproduktivní

  • Uvozovky kolem uvozovek
    echo "Result: "$text""
  • Potřebuji jednotlivá slova nebo globbing
    IFS=:; find $PATH -name command
  • Interpretace ERE/SP v [[ nebo case
    SP=*.txt ; ERE=^a.*b$
    [[ $var == $SP ]]
    [[ $var =~ $ERE ]]
    case $var in    [a-z]|"") …;;    "*") …;;    *) …;; esac

Časté problémy a jejich řešení

Procházení seznamu souborů

Procházení seznamu souborů

for file in $( ls ); …

Procházení seznamu souborů

for file in $( ls ); …
for file in *; …

Zpracování vstupních dat

Zpracování vstupních dat

cat file | while read; do …; done

Zpracování vstupních dat

cat file | while read; do …; done
while read; do …; done <file

Čtení jedné řádky ze vstupu

Čtení jedné řádky ze vstupu

read line; declare -p line

Čtení jedné řádky ze vstupu

read line; declare -p line
read; declare -p REPLY

Přidání hodnoty do proměnné

Přidání hodnoty do proměnné

echo $PATH

Přidání hodnoty do proměnné

$( echo $PATH )

Přidání hodnoty do proměnné

PATH=$( echo $PATH ):/new/dir

Přidání hodnoty do proměnné

PATH=$( echo $PATH ):/new/dir
PATH=$PATH:/new/dir

Přidání hodnoty do proměnné

PATH=$( echo $PATH ):/new/dir
PATH=$PATH:/new/dir
PATH+=:/new/dir

Rozdělení řetězce

Rozdělení řetězce

echo $PATH

Rozdělení řetězce

printf '%s\n' $( echo $PATH | tr : ' ' )

Rozdělení řetězce

printf '%s\n' $( echo $PATH | tr : ' ' )
printf '%s\n' $( tr : ' ' <<<$PATH )

Rozdělení řetězce

printf '%s\n' $( echo $PATH | tr : ' ' )
printf '%s\n' $( tr : ' ' <<<$PATH )
printf '%s\n' ${PATH//:/ }

Rozdělení řetězce

printf '%s\n' $( echo $PATH | tr : ' ' )
printf '%s\n' $( tr : ' ' <<<$PATH )
printf '%s\n' ${PATH//:/ }
printf %s\\n ${PATH//:/ }

Rozdělení řetězce

printf '%s\n' $( echo $PATH | tr : ' ' )
printf '%s\n' $( tr : ' ' <<<$PATH )
printf '%s\n' ${PATH//:/ }
printf %s\\n ${PATH//:/ }
IFS=:; printf %s\\n $PATH

Zpracování argumentů

Zpracování argumentů

for arg in $*; …

Zpracování argumentů

for arg in $*; …
for arg in "$@"; …

Zpracování argumentů

for arg in $*; …
for arg in "$@"; …
for arg; …

Použití názvu skriptu

Použití názvu skriptu

echo "my_script: …"

Použití názvu skriptu

echo "my_script: …"
echo "$0: …"

Úspornější zápisy

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota if [ -z "$var" ]; then var=default; fi
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota if [ -z "$var" ]; then var=default; fi [ -z "$var" ] && var=default
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota if [ -z "$var" ]; then var=default; fi [ -z "$var" ] && var=default : ${var:=defaul}
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
    var=$( echo "$var" | sed 's/foo/bar/' ) var=$( echo "$var" | sed 's/foo/bar/g' )
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
    var=$( echo "$var" | sed 's/foo/bar/' ) var=$( echo "$var" | sed 's/foo/bar/g' ) var=${var/foo/bar} var=${var//foo/bar}
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez echo "$var" | sed 's/^foo//' echo "$var" | sed 's/foo$//'
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez echo "$var" | sed 's/^foo//' echo "$var" | sed 's/foo$//' echo "${var#foo}" echo "${var%foo}"
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez
  • Délka

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez
  • Délka len=$( echo -n "$var" | wc -c )

Práce s proměnnou

  • Výchozí/alternativní hodnota
  • Náhrada
  • Ořez
  • Délka len=$( echo -n "$var" | wc -c ) len=${#var}

Seznam hodnot ve skalární proměnné Vs v poli


							for i in "$@"; do
							   if [ -d "$i" ]; then dirs="$dirs $i"; fi
							done
							ls -ld $dirs
						


							for i; do
							   [ -d "$i" ] && dirs+=( "$i" )
							done
							ls -ld "${dirs[@]}"
						

Seznam hodnot ve skalární proměnné Vs v poli


							var=''
							for arg; do var+=,$arg; done
							echo "$var" | sed 's/^,//'
							echo "$var" | sed 's/,//'
							echo "$var" | sed s/,//
							echo "$var" | sed 1s/,//
						


							var=()
							for arg; do var+=( "$arg" ); done
							IFS=,; echo "${var[*]}"
						

Testování podmínek běhu


							if podmínka1; then
							   dělej
							else
							   chyba
							   exit 1
							fi
						

Testování podmínek běhu


							if podmínka1; then
							   if podmínka2; then
							      dělej
							   else
							      chyba
							      exit 1
							   fi
							else
							   chyba
							   exit 1
							fi
						

Testování podmínek běhu


							if ! podmínka1; then
							   chyba
							   exit 1
							fi

							if ! podmínka2; then
							   chyba
							   exit 1
							fi

							dělej
						

Testování podmínek běhu


							err() { chyba; exit 1; }

							if ! podmínka1; then
							   chyba
							   exit 1
							fi

							if ! podmínka2; then
							   chyba
							   exit 1
							fi

							dělej
						

Testování podmínek běhu


							err() { chyba; exit 1; }

							if ! podmínka1; then
							   chyba
							   exit 1
							fi

							podmínka2 || err

							dělej
						

Testování podmínek běhu


							err() { chyba; exit 1; }

							podmínka1 || err
							podmínka2 || err

							dělej
						

Úspornější zápisy příkazů

  • Kratší jména
  • Implicitní chování
  • Proměnné prostředí
  • Expanze
  • Opakované použití

Kratší jména

  • test …[[ … ]][ … ]
  • source file. file
  • $(cmd)`cmd`
  • function ff()
  • date +%Y-%m-%d_%H:%M:%Sdate +%F_%T
  • grep -Eegrep
  • gzip -dgunzip

Implicitní chování 1

  • cat file | filterfilter <file
    filter file
  • read a proměnná REPLY
  • [ -z string ][ string ]
  • (( value != 0 ))(( value ))
  • for i in "$@"for i
  • select i in "$@"select i

Implicitní chování 2

  • cmd; if [ $? -eq 0 ]; then …; fi
    if cmd; then …; fi
    cmd && …
  • declare -i var; var=foo
  • declare -i var; var+=1
  • kill last_jobkill %+
  • fg %+fg

Proměnné prostředí

  • $(pwd)$PWD~+
  • $(whoami)$USER
  • $HOME~
  • $CDPATH
  • export LESS=-eX; less file

Expanze

  • Kombinace a posloupnosti Brace expansion {a..z} {00..100..10} file.{txt,old} text{,,,}
  • Jména souborů Pathname exapnsion (globbing) * ? []
  • Rozšířené shellovské vzory Extended Shell Patterns shopt -s extglob
    !(list|…) ?(list|…) @(list|…)
    *(list|…) +(list|…)

Opakované použití

  • Proměnné
    f=/foo/bar/baz.xyz; cmd "$f"
  • Funkce
    f(){ cmd;}
    f()(cmd)
  • Aliasy
    alias c=cmd
    c

Zpět k původní motivaci


							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							<slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							<slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							grep -Ex '.{5}'<slovnik.txt | grep "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							<slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							grep -Ex '.{5}'<slovnik.txt | grep "e.i.e"
							grep -Ex '.{5}' slovnik.txt | grep "e.i.e"
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							<slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							grep -Ex '.{5}'<slovnik.txt | grep "e.i.e"
							grep -Ex '.{5}' slovnik.txt | grep "e.i.e"
							grep -Ex .{5} slovnik.txt | grep e.i.e
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							<slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							grep -Ex '.{5}'<slovnik.txt | grep "e.i.e"
							grep -Ex '.{5}' slovnik.txt | grep "e.i.e"
							grep -Ex .{5} slovnik.txt | grep e.i.e
							grep -Ex .{5} slovnik.txt|grep e.i.e
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							<slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							grep -Ex '.{5}'<slovnik.txt | grep "e.i.e"
							grep -Ex '.{5}' slovnik.txt | grep "e.i.e"
							grep -Ex .{5} slovnik.txt | grep e.i.e
							grep -Ex .{5} slovnik.txt|grep e.i.e
							egrep ^.{5}$ slovnik.txt|grep e.i.e
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | grep -e "e.i.e"
							< slovnik.txt grep -ex '.\{5}\' | egrep "e.i.e"
							< slovnik.txt grep -x '.\{5}\' | grep "e.i.e"
							< slovnik.txt grep -x '.....' | grep "e.i.e"
							< slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							<slovnik.txt grep -Ex '.{5}' | grep "e.i.e"
							grep -Ex '.{5}'<slovnik.txt | grep "e.i.e"
							grep -Ex '.{5}' slovnik.txt | grep "e.i.e"
							grep -Ex .{5} slovnik.txt | grep e.i.e
							grep -Ex .{5} slovnik.txt|grep e.i.e
							egrep ^.{5}$ slovnik.txt|grep e.i.e
							egrep ^.{5}$ *.txt|grep e.i.e
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							egrep ^.{5}$ slovnik.txt|grep e.i.e
							egrep ^.{5}$ *.txt|grep e.i.e
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							egrep ^.{5}$ *.txt|grep e.i.e

							alias w='grep -Ex .{5} *.txt|grep'
							w(){ egrep ^.{5}$ *.txt|grep "$1";}
							w()(egrep ^.{5}$ *.txt|grep "$@")
							w e.i.e
						

							< slovnik.txt grep -e '^.\{5}\$' | grep -e "^e.i.e"
							egrep ^.{5}$ *.txt|grep e.i.e

							alias w='grep -Ex .{5} *.txt|grep'
							w(){ egrep ^.{5}$ *.txt|grep "$1";}
							w()(egrep ^.{5}$ *.txt|grep "$@")
							w e.i.e

							alias a=alias
							a g=egrep
							a w='g ^.{5}$ *.txt|g'
							w e.i.e
							w e.i.e|g x
						

?


https://lukasbarinka.gitlab.io/if22