Linux за българи: Форуми

Програмиране => Общ форум => Темата е започната от: h4rd2kill в Mar 19, 2012, 22:39



Титла: Bourn shell скрипт
Публикувано от: h4rd2kill в Mar 19, 2012, 22:39
Здравейте,

Както виждате съм нов във форума и като цяло нов в линукса. Надявам се да не сте много строги с мен ;). Имам задание за линукс скрипт и определено ми трябва помощ.
Решението трябва да бъде преносим Bourne shell скрипт. Не могат да се използват специфични неща от модерните shell-ове като bash, ksh, zsh и т.н.
Ето и задачката:

Да се напише скрипт, който приема като аргумент файл, чието съдържание представлява списък от пълни пътища до файлове, които трябва да бъдат архивирани в tar архив и компресирани. Резултатът от компресията трябва да бъде копиран през SSH на отдалечена машина. Необходимо е да се поддържат следните опции на командния ред:
-f - файл, от който да се четат пътищата към файловете, който ще се архивират
-е - ако някой от пътищата не съществува изпълнението на скрипта се преустановява и се извежда съобщение за грешка; в противен случай несъществуващ път се игнорира
-g - gzip компресия; в противен случай bzip2
-h - отдалечен хост и потребител във вид: user@host
За копиране през SSH може да се използва scp командата, която интерактивно очаква въвеждане на парола (приемайки, че автентикацията е само чрез потребителско име и парола).

Всяка насока или пример ще ми бъде от полза.
Мерси предварително.


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Mar 20, 2012, 00:09
Написах го така, че да работи с bourne shell.
Съвет: ако можеш, не използвай bourne shell!

Код:
#!/bin/sh

_fname= _errexit= _zip= _user_host=
_zip=/bin/bzip2
_ts=`date +%Y%m%d_%H%M%S`
_ext=bz2

_usage_msg="

usage: $0 -f <file_name>  -h user@host [ -e | -g ]

  -f file name containing pathnames
  -e exit if path does not exists
  -g use gzip (otherwise bzip2)
  -h remote user@host

"

die() {
  printf >&2 '%s\n' "$@"
  exit 1
  }

[ $# -lt 1 ] && die "$_usage_msg"

[ -f /tmp/_kill_byte_ ] && rm /tmp/_kill_byte_

set -- `getopt f:egh: "$@"`

[ $? -ne 0 ] && die "$_usage_msg"

while [ $# -gt 0 ]; do
  case $1 in
     -f ) shift;  _fname=$1; shift ;;
     -e ) shift; _errexit=on ;;
     -g ) shift; _zip=/bin/gzip _ext=gz ;;
     -h ) shift; _user_host=$1; shift ;;
     -- ) break ;;
  esac
done

[ -r "$_fname" ] || die "invalid filename: $_fname"

case $_user_host in
   *@* )                                 ;;
    *  ) die "invalid host: $_user_host" ;;
esac

touch arch_"$_ts".tar

while IFS= read f; do
  [ -f "$f" ] || {
    [ "$_errexit" ] &&
      printf '%s\n' "$f" >  /tmp/_kill_byte_
    continue
     }
  tar uf arch_"$_ts".tar "$f"
done < "$_fname"

[ -f /tmp/_kill_byte_ ] && die "invalid path: `cat /tmp/_kill_byte_`"

"$_zip" arch_"$_ts".tar ||
  die 'compressing archive file failed'

scp arch_"$_ts.tar.$_ext" "$_user_host":



Титла: Re: Bourn shell скрипт
Публикувано от: b2l в Mar 20, 2012, 08:58
@h4rd2kill интересно ми е сега като те изпитат на този скрипт какво ще кажеш. Та ти един ред от него сигурно не разбираш...


Титла: Re: Bourn shell скрипт
Публикувано от: h4rd2kill в Mar 20, 2012, 09:27
@radoulov Мерси много за отделеното време и труд. Започвам да го разглеждам и тествам подробно..
@b2l Бъркаш се .. знам поне 1 ред от скрипта... (#!/bin/sh)  8). В кръгъ на шегата разбира се  и мерси, че се притесняваш за мен как ще го защитавам .. :) Ако искаш да помогнеш можеш да обясниш подробно логиката на всеки ред :o  [_]3


Титла: Re: Bourn shell скрипт
Публикувано от: b2l в Mar 20, 2012, 09:30
@b2l Бъркаш се .. знам поне 1 ред от скрипта... (#!/bin/sh)  8). В кръгъ на шегата разбира се  и мерси, че се притесняваш за мен как ще го защитавам .. :) Ако искаш да помогнеш можеш да обясниш подробно логиката на всеки ред :o  [_]3

А ти ако искаш може да ме хванеш и да ме поразходиш :D. Едно време и аз бях нагъл като теб (не че сега не съм де), затова няма да ти обяснявам нищо :D.


Титла: Re: Bourn shell скрипт
Публикувано от: h4rd2kill в Mar 20, 2012, 09:42
@b2l Бъркаш се .. знам поне 1 ред от скрипта... (#!/bin/sh)  8). В кръгъ на шегата разбира се  и мерси, че се притесняваш за мен как ще го защитавам .. :) Ако искаш да помогнеш можеш да обясниш подробно логиката на всеки ред :o  [_]3

А ти ако искаш може да ме хванеш и да ме поразходиш :D. Едно време и аз бях нагъл като теб (не че сега не съм де), затова няма да ти обяснявам нищо :D.

Изобщо не съм нагъл... просто помолих за помощ без да задължавам никой. Мисля, че това вече се превръща в офф топик ..


Титла: Re: Bourn shell скрипт
Публикувано от: bop_bop_mara в Mar 20, 2012, 13:00
Мисля, че това вече се превръща в офф топик ..
Определено...

За да се върнем донякъде на темата - сериозно, има ли някой тука, който да има наличен оригинален Bourne Shell, че да се тества  ;D

@h4rd2kill - моят съвет е да си отвориш posix спецификациите:
http://pubs.opengroup.org/onlinepubs/007904875/utilities/contents.html
и да следиш за съвместимост на програмите ти с тях.

@radoulov - може ли обяснение/линк относно конвенцията, която използваш за именуване на променливите (за пръв път виждам така в shell script), и обяснение на while IFS= read f?


Титла: Re: Bourn shell скрипт
Публикувано от: laskov в Mar 20, 2012, 13:15
//Малко извън темата: h4rd2kill, radoulov е пропуснал да те предупреди, затова ще го направя аз: На radoulov скриптовете не са просто за 6-ца в училище. Те са висш пилотаж в писането на скриптове и малко хора могат да пишат така, а за начинаещ това е съвсем невъзможно. Така че, ако решиш да го представиш, ще трябва добре да се подготвиш и ще трябва за в бъдеще да поддържаш (доколкото би могъл) нивото. Успех!


Титла: Re: Bourn shell скрипт
Публикувано от: h4rd2kill в Mar 20, 2012, 13:41
Мерси за съвета @laskov. Това определено ще бъде еднократно представяне и надявам се няма да имам бъдещи срещи с преподавателя :D.

@bop_bop_mara  не знам дали съм те разбрал точно, но към скрипта няма да се използват никакви допалнителни програми просто трябва да изпълнява това което му е зададено.
Ще е полезно малко обяснение от автора или конвертиране към по- простичък вариант ;).

Мерси отново.


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Mar 20, 2012, 23:09
Здравей @bop_bop_mara,

Цитат
[...] може ли обяснение/линк относно конвенцията,
която използваш за именуване на променливите (за пръв път виждам така в shell script)

Не използвам uppercase за да избегна евентуални clashes със системни променливи (PATH, HOSTNAME, LOGNAME и т.н.).
Използвам _varname (или _varname_) - въпрос на стил (и за да избегна clashes с други custom променливи).

Май беше идея на Chris F. A Johnson - http://www.amazon.com/Shell-Scripting-Recipes-Problem-Solution-Approach/dp/1590594711

Цитат
и обяснение на while IFS= read f?

Код:
IFS=

Правя временно (по време на read) reset на IFS (internal field separator),
за да избегна евентуални проблеми с имена на фаилове, които съдържат IFS символи:

Код:
$ _fn='      fname     '
$ printf '%s\n' "$_fn" |
>   while read v; do
>   printf 'one:\n|%s|\n' "$v"
> done
one:
|fname|
$ printf '%s\n' "$_fn" |
>   while IFS= read v; do
>     printf 'two:\n|%s|\n' "$v"
> done
two:
|      fname     |

Здравей @laskov,
благодаря за хубавите думи, не заслужавам чак толкова :)

Здравей @h4rd2kill,
ще ми е по-лесно ако ме питаш за конкретни части от кода ...


Титла: Re: Bourn shell скрипт
Публикувано от: b2l в Mar 20, 2012, 23:33
Така! Време е да си натреса тролското мнение! Някой ще ми каже ли защо му пишете домашното?!


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Mar 21, 2012, 00:13
Защото смятам, че така, както го написах, h4rd2kill може да научи повече.
Надявам се да не греша ...


Титла: Re: Bourn shell скрипт
Публикувано от: h4rd2kill в Mar 21, 2012, 10:28
Здравей @radoulov

Бих искал да питам следното:

В скрипта ползваш
Код:
touch arch_"$_ts".tar
.........
.........
  tar uf arch_"$_ts".tar "$f"
както разбирам това ще създаде .tar файл и после с втората команда го update- ва файала.
Въпроса е, защо не се ползва -uf  а само uf.
Също така разширението (.tar.$_ext ) се слага чак накрая с изпращането чрез scp.  Каква е идеята на този метод ?


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Mar 21, 2012, 11:20
Цитат
Код:
touch arch_"$_ts".tar
.........
.........
tar uf arch_"$_ts".tar "$f"

както разбирам това ще създаде .tar файл и после с втората команда го update- ва файала.

Точно така. Някои верси на tar автоматично създават tar archive-а, ако не съществува
(в тези случаи не е необходимо да правиш touch archivename преди update).

Цитат
Въпроса е, защо не се ползва -uf  а само uf.

Това няма значение, можеш да използваш -option и само option, tar разбира и двете.

Цитат
Също така разширението (.tar.$_ext ) се слага чак накрая с изпращането чрез scp.  Каква е идеята на този метод ?

_ext е променлива: default bz2, ако си изпълнил командата s -g: gz.
По този начин scp намира точния файл.


Титла: Re: Bourn shell скрипт
Публикувано от: h4rd2kill в Mar 23, 2012, 13:00
Мерси @radulov .

Може ли малко обяснение какво правят тези части:

Код:
 [ $# -lt 1 ] && die "$_usage_msg" 

.....

Код:
 [ $? -ne 0 ] && die "$_usage_msg" 

Предполагам, че прави проверка спрямо зададения начин на използване, но не съм сигурен каква е точно проверката.

Поздрави.


Титла: Re: Bourn shell скрипт
Публикувано от: bop_bop_mara в Mar 23, 2012, 14:00
Специални параметри - http://pubs.opengroup.org/onlinepubs/007904875/utilities/xcu_chap02.html#tag_02_05
Списъци с && - http://pubs.opengroup.org/onlinepubs/007904875/utilities/xcu_chap02.html#tag_02_09_03_06
Команда test - http://pubs.opengroup.org/onlinepubs/007904875/utilities/test.html


Титла: Re: Bourn shell скрипт
Публикувано от: h4rd2kill в Apr 08, 2012, 12:23
Здравейте,

Може ли да дадете някаква документация или обяснение както точно прави този ред:
Код:
 set -- `getopt f:egh: "$@"`

До колкото четох това управлява командите, които се подавата.
Така в нашия случай f: изисква аргумент и h: също.
После не ми е точно ясно какво се случва с "$@" .

Мерси.


Титла: Re: Bourn shell скрипт
Публикувано от: dev_urandom в Apr 08, 2012, 18:08
Здравейте,

Може ли да дадете някаква документация или обяснение както точно прави този ред:
Код:
 set -- `getopt f:egh: "$@"`

До колкото четох това управлява командите, които се подавата.
Така в нашия случай f: изисква аргумент и h: също.
После не ми е точно ясно какво се случва с "$@" .

Мерси.

http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Apr 08, 2012, 22:58
Цитат
До колкото четох това управлява командите, които се подавата.
Така в нашия случай f: изисква аргумент и h: също.

Правилно си разбрал.
Използвам getopt, за compatibility с bourne shell (bash, zsh и korn shell имат builtin getopts).
Виж man getopt за повече информация.


Виж също shelldorado ($2) (потърси Using "getopt").

Цитат
После не ми е точно ясно какво се случва с "$@" .

Обработват се един след друг (shift прави $2 -> $1,  $3 -> $2 и т.н.).
По този начин работим винаги с $1.

Код:
while [ $# -gt 0 ]; do
  case $1 in
 ...

И междувременно задаваме стойности на променливите _fname, _errexit, _zip и т.н.





Титла: Re: Bourn shell скрипт
Публикувано от: gat3way в Apr 09, 2012, 00:47
Ей, внимателно с това:

Цитат
[ -f /tmp/_kill_byte_ ] && rm /tmp/_kill_byte_

Някой шегаджия може да сътвори неприятни неща като symlink-ва /tmp/_kill_byte_ към нещо и чака root-а да изпълни скрипта за да го затрие :)

Колкото и да е тъпо, проверката с -f минава, ако симлинка сочи към истински съществуващ файл. И тъй като е ясно как трябва да се казва линка...:)


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Apr 09, 2012, 01:35
Ей, внимателно с това:

Цитат
[ -f /tmp/_kill_byte_ ] && rm /tmp/_kill_byte_

Някой шегаджия може да сътвори неприятни неща като symlink-ва /tmp/_kill_byte_ към нещо и чака root-а да изпълни скрипта за да го затрие :)

Колкото и да е тъпо, проверката с -f минава, ако симлинка сочи към истински съществуващ файл. И тъй като е ясно как трябва да се казва линка...:)

Good point!
И всичко това, защото bourne shell изпълнява while цикъла в subshell ...

По-добре би било:

Код:
[ -L  /tmp/_kill_byte_ ] || {
  [ -f /tmp/_kill_byte_ ] &&
    rm /tmp/_kill_byte_
    }
     



Титла: Re: Bourn shell скрипт
Публикувано от: gat3way в Apr 09, 2012, 01:50
Теоретично, дори така е възможно да race-не ако се създаде линка в много правилния момент. Примерно понеже после се пише в същият файл , можеш да се възползваш от това като линкнеш файла след проверката. Как - ами примерно пишеш програма, ползваща inotify, създаваш /tmp/_kill_byte (не symlink, а обикновен файл), чакаш за inotify събитието "изтриване на /tmp/_kill_byte" и го симлинкваш веднага след това. Последвалото писане в този файл ще помаже това, към което си го симлинкнал. Имаш добри шансове да успееш, шансовете се покачват на многоядрена машина :)

Но пък това става малко висш пилотаж на мизерията, трябва да е наистина голям шегаджия :)

Генерално решение иначе има - просто ползваш mktemp за временния файл и влошаваш живота на шегаджията доволно. Сега освен че трябва да направи този номер, трябва и да bruteforce-не случайното име на tempfile-а, нещо което вдига сложността с порядъци и го прави доста непрактично.


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Apr 09, 2012, 11:57
Да,
с mktemp ще е много по-сигурно.


Титла: Re: Bourn shell скрипт
Публикувано от: sdr в Apr 10, 2012, 12:08
при наличие на inotify за промяна в директорията колко по-труно ще бъде :)


Титла: Re: Bourn shell скрипт
Публикувано от: gat3way в Apr 10, 2012, 13:04
Доста по-трудно.

Kогато получиш IN_CREATE нотификацията, файлът вече е създаден и собственост на root (няма как да го затриеш, защото /tmp обикновено е със sticky бит). Т.е нищо не можеш да направиш вече.

Виж обаче ако можеше да го изтриеш, нещата щяха определено да загрубеят, тогава можеш да се възползваш от inotify за да затриеш и симлинкнеш tempfile-а....и накрая ще мине онзи ред в скрипта дето го трие и бум :)


Титла: Re: Bourn shell скрипт
Публикувано от: Naka в Apr 10, 2012, 19:52
//off

Сега като му шибнат някоя тройка :'( на скрипта на radoulov, ще ви пропаднат теориите за хубав скрипт. ;D
 [_]3


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Apr 10, 2012, 20:14
//off

Сега като му шибнат някоя тройка :'( на скрипта на radoulov, ще ви пропаднат теориите за хубав скрипт. ;D
 [_]3

Точно така.
Аз лично, не съм претендирал, че е хубав :)

П.С. И ако знех, че такова внимание ще му бъде обърнато,
щях да се постарая повече :)


Титла: Re: Bourn shell скрипт
Публикувано от: Naka в Apr 10, 2012, 20:42

Точно така.
Аз лично, не съм претендирал, че е хубав :)

Ама ако му пишат 6, да вземе да почерпиш виртуално.


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Apr 10, 2012, 20:47
Ама ако му пишат 6, да вземе да почерпиш виртуално.

Абе 6 няма да му пишат,
ама ако послужи за нещо, ще почерпя :)


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Apr 10, 2012, 22:24
Много приказки са излишни :)
Нова версия:

Код:
#!/bin/sh

_fname= _errexit= _zip= _user_host=
_zip=/bin/bzip2
_ts=`date +%Y%m%d_%H%M%S`
_ext=bz2

_usage_msg="

usage: $0 -f <file_name>  -h user@host [ -e | -g ]

  -f file name containing pathnames
  -e exit if path does not exists
  -g use gzip (otherwise bzip2)
  -h remote user@host

"

die() {
  printf >&2 '%s\n' "$@"
  exit 1
  }

[ $# -lt 1 ] && die "$_usage_msg"

# This commands do not work with all shells,
# because the set command does not always return an error code
# if getopt fails
set -- `getopt f:egh: "$@"` ||
  die "$_usage_msg"

while [ $# -gt 0 ]; do
  case $1 in
     -f ) shift;  _fname=$1; shift ;;
     -e ) shift; _errexit=on ;;
     -g ) shift; _zip=/bin/gzip _ext=gz ;;
     -h ) shift; _user_host=$1; shift ;;
     -- ) break ;;
  esac
done

[ -r "$_fname" ] || die "invalid filename: $_fname"

case $_user_host in
   *@* )                                 ;;
    *  ) die "invalid host: $_user_host" ;;
esac

touch arch_"$_ts".tar

while IFS= read f; do
  [ -f "$f" ] && [ ! -L "$f" ] || {
    [ "$_errexit" ] &&
      die "invalid path: $f" ||
     continue
     }
  tar uf arch_"$_ts".tar "$f" ||
    die 'tar failed'
done < "$_fname" ||
  exit 1


"$_zip" arch_"$_ts".tar ||
  die 'compressing archive file failed'

scp arch_"$_ts.tar.$_ext" "$_user_host": ||
  die 'scp failed'




Прочитайки отново пост #1, реших, че може да не съм разбрал правилно.
Въпросът е с повишена трудност, ако, както бях разбрал, скриптът трябва да работи
със старата Bourne shell (за да съм по-ясен: /bin/shell на Solaris
[the old Bourne shell], не /usr/bin/shell на HP-UX [която всъщност е ksh88]).

Ако в действителност изискването е скриптът да е стандартен/POSIX - нещата стават
мнооого по-лесни :)

С -e в някой случаи може да остане (частичен) tar file.
Ако това не е желателно, виждайки, че сме малко параноични,
ще трябва да се работи първо върху временен file
(име генерирано с mktemp :)) който да се преименува само след успешно архивиране.



Титла: Re: Bourn shell скрипт
Публикувано от: h4rd2kill в Jun 21, 2012, 12:58
Само да Ви информирам, че след значително усaкатяване на скрипта и нагласяне за моито ниво имам 6 :)
Мерси на всички за информацията и най- вече на @radoulov


Титла: Re: Bourn shell скрипт
Публикувано от: radoulov в Jun 21, 2012, 13:00
Браво!
Благодаря за feedback-а!