mironov_orig 09.08.2011 12:20

Tips & tricksshell-штучки-дрючки

Выкидываю заметку из черновиков как есть.
Для начала оговоримся, что мы имеем дело именно с башем — со скриптами под свои нужды, автоматизацией рутинных действий и т.п. никаких загрузочных скриптов и прочего restricted posix shell.

Если вам не нужна переносимость (между ОС (*nix, *BSD, Solaris, etc) и шелами (sh, dash, bash, zsh, tcsh, ksh etc)), вы пишите не скрипт инициализации для SysVinit а просто скрипт для личных и не очень нужд, имеет смысл не ограничивать себя рамками posix-совместимого шела. Нижеследующие советы предлагаются к прочтению всем, кто не читал man bash или соответствующую главу POSIX'a.

Сравнения

1
if [[ условие ]]; then

Особенности этой конструкции в баше:
1. внутри [[ ]] не действует «разбиение на слова» (word spliting) ⇒ мы спокойно можем использовать переменные без «заковычивания», если в строке есть пробел, ничего страшного не случится.
2. спецсимволы *, ? и [ воспринимаются как обычные символы (никакого глобинга)
3. всё остальное действует

Арифметика

1
2
3
4
5
let i=0
let i++
let i--
let i=10*20
let i**2 #возведение в степень

Кроме того
  • унарные - и +
  • ++var, --var
  • var++, var--
  • ! и ~ логическое и побитовое отрицание
  • % - остаток от деления
  • << >> побитовые сдвиги
  • =, >=, <=
  • ==, !=
  • &, |, ^ побитовые и, или, исключающее или
  • &&, || логические и, или
  • операции через запятую
  • expr?expr:expr
  • поддержка восьми- и шестнадцатибитныз чисел(в формате с ведущим нулём)
  • поддержка чисел в системах счисления с основанием от 2 до 64 (base#n)
Забудьте про posix $(()) и i=$(expr $i + 1)
Если вам нужно, в баше есть арифметический цикл for
1
for ((i=0; i<10; i++)); do echo $i; done

Вместо seq 1 10 в баше есть {1..10}

Дополнительно

В баше есть такая штука
1
select choice in 'wipe ur disk' ' shutdown'; do ; done

Не баш-специфичные, но, тем не менее, малоизвестные возможности (POSIX-совместимо):
1. Использование значения по-умолчанию
1
${parameter:-default}

Если parameter не существует или пуст, иначе будет использовано значение default. [если переменная не существовала, она не создаётся; если переменная была пуста, она пустой и остаётся]
2. Присвоение значения по умолчанию
1
${param:=default}

Если param не существует или пуст, создаётся соответствующая переменная/param присваивается default, иначе — используется значение param.
3. Отобразить ошибку, если переменная не существует или пуста
1
2
3
4
5
6
7
8
unset param
${param?}
bash: param: paramameter null or not set
${param?Забыли вы про param}
bash: param: Забыли вы про param
param='value'
echo ${param?}
value

4. Использовать альтернативное значение
1
${param:+another value}

Если param не существует или пуста, ничего не будет подставлено, иначе будет подставлено another value
5. Вывод подстроки
1
2
${param:offset}
${param:offset:length}

Выводит подстроку начинающуюся с позиции offset длиной length, если длина не указана - до конца.
8. Длина строки
1
2
3
4
${#param} #длина строки
${#*} #количество переданных параметров
${#@} #тоже самое
${#param} #param - массив ⇒ количество элементов


9. Удаление префикса
 1
2
3
4
5
6
7
8
9
10
11
${param#prefix} #удаление наименьшего соответсвия
${param##prefix} #удаление наибольшего соответствия

# пример
$ param='backup.tar.gz'
$ echo ${param}
backup.tar.gz
$ echo ${param#*.}
tar.gz
$ echo ${param##*.}
gz

10. Удаление суффикса
 1
2
3
4
5
6
7
8
9
10
${param%suffix} #удаление наименьшего постфикса
${param%%suffix} #наибольшего

# пример
$ echo ${param}
backup.tar.gz
$ echo ${param%.*}
backup.tar
$ echo ${param%%.*}
backup

11. Замена
1
2
${param/pattern/string}  # обрабатывается только первое вхождение
${param//pattern/string} # обрабатываются все вхождения


12. Изменение регистра
1
2
3
4
${param^pattern}
${param^^pattern}
${param,pattern}
${param,,pattern}

Изменяет регистр символа pattern, «^» - на верхний, «,» - на нижний. Если pattern отсутствует, изменяет регистр первого символа. «^^» и «,,» изменяет регистр всех символов совпадающих с pattern в строке. Если pattern пуст, изменяет регистр всех символов. Если param - массив, операция применяется ко всем элементам массива по очереди.

Литература

1. man bash
2. Shell Command Language


Тэги: bash man sh shell
+ 21 -
Похожие Поделиться

exelens 09.08.2011 12:26 #
Напиши плиз про регулярки и циклы.
С примерами если можно.
mironov_orig 09.08.2011 12:27 #
Не понял вопроса, распиши конкретнее
exelens 09.08.2011 12:31 #
Например.. у тебя есть страница в html, которую нужно распарсить и оставить только
tr td th table body head html a href rowspan colspan

всё остальное удалить. Подобный, работающий скрипт на питоне мне написал NVBN.

from BeautifulSoup import BeautifulSoup
import urllib, sys

def parse(value, valid_tags, valid_attrs):
valid_tags = valid_tags.split()
valid_attrs = valid_attrs.split()
soup = BeautifulSoup(value)
for tag in soup.findAll(True):
if tag.name not in valid_tags:
tag.hidden = True
if tag.name == 'script':
tag.content = ''
else:
tag.attrs = [(attr, val) for attr, val in tag.attrs if attr in valid_attrs]
return soup.renderContents()


if __name__ == '__main__':
data = urllib.urlopen(sys.argv[1]).read()
print parse(data, 'tr td th table body head html a', 'href rowspan colspan').replace('head>', """head>
'
""")


А ты можешь сделать такое на баше?
mironov_orig 09.08.2011 12:46 #
Можно, но для подобного лучше perl|python с какой-нить красивой либой
exelens 09.08.2011 12:49 #
Понял
blackraven 09.08.2011 15:13 #
Or awk...
mironov_orig 09.08.2011 18:47 #
Предложи рабочее решение, очень интересно посмотреть.
blackraven 11.08.2011 20:14 #
Pisal na proshloj rabote... V principe - nichego slozhnogo, no dovol'no neuniversal'no i mnogoslovno.
Lenivo eshe raz pisat'.
Bkmz 09.08.2011 13:38 #
lxml.html имхо както удачнее чтоли, или привычнее, ну вы на него посмотрите, под питоном самая лучшая либа.
Под С вообще самая лучшая =)
IT.Tux.Droid 15.08.2011 21:53 #
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/usr/bin/python
# coding: utf-8
import urllib, re

def get_tags(url, res = []):
        data = urllib.urlopen(url).read()
        tags = re.findall("<([a-z]+)",data)
        for tag in tags:
                if tag not in res:
                        res.append(tag)
        return res

print get_tags("http://welinux.ru/")

['html', 'head', 'meta', 'link', 'script', 'title', 'body', 'div', 'ol', 'li', 'a', 'span', 'h', 'img', 'br', 'table', 'tr', 'td', 'ul', 'form', 'input', 'noscript']

Не на баше, но регуляркам же все-равно =)
mironov_orig 21.08.2011 12:12 #
Ты не так понял задачу. Надо оставить не только сами теги, но и то, что между ними =)
IT.Tux.Droid 29.08.2011 21:42 #
... и оставить только

tr td th table body head html a href rowspan colspan

всё остальное удалить.


А в чем же тогда профит? =)

PS: в своем решении профита тоже не нашел.
mironov_orig 29.08.2011 22:54 #
Хз в чём профит, спроси у «заказчика» ☺
wilful 09.08.2011 13:06 #
enot 09.08.2011 18:24 #
это перевод, знаменитого ABSG. К тому же, наверняка, несвежий. Рекоммендую читать в оригинале.
enot 09.08.2011 18:30 #
если верить номеру ревизии в ссылке на странице, это перевод версии ABS 1.8 (15 May 2003 14:52) - еще тот труп. Текущая версия 6.3 от 2 мая 2011

вот кстати ABSG на фрешмите:
http://freshmeat.net/projects/advancedbashscriptingguide
wilful 09.08.2011 20:07 #
Баш реально сильно изменился))
enot 10.08.2011 14:38 #
за 9 лет-то да, появилось много новых фич, о которых стоило бы знать.
wilful 10.08.2011 14:46 #
Вот и напишите об этом (http://welinux.ru/post/6369/), мне этого гайда за глаза хватает, и всё работает. Но вот обновлений bash помню по пальцам... чтобы что-то такое координальное... не, не видел.
mironov_orig 09.08.2011 18:47 #
Дык не в гайдах дело. однократное полное прочтение man bash покроет 90% потребностей, только вот никто маны особо не читает.
zb 09.08.2011 18:49 #
"не каждый пользователь дочитает до середины документации..." (c)
enot 10.08.2011 14:41 #
я бы сказал не каждый пользователь догадывается о ее существовании. для гуевых утилит это явление особо распространено.
enot 10.08.2011 14:38 #
я не призываю никого читать ман баш от корки до корки. ABSG скорее сборник рецептов. я к нему обращаюсь по мере возникновения вопросов. все тобой описанное в статье там приводится с примерами.
mealsforall 09.08.2011 14:38 #
Оч хорошо!
mult 12.08.2011 07:28 #
офтоп, конечно же, но заголовок точно без ошибок?
mironov_orig 12.08.2011 08:10 #
Who cares?
mult 12.08.2011 13:41 #
вот так лучше :)
ananas 12.08.2011 09:51 #
и с какой это радости я должен забывать про $((...)), если он мне дает результат арифметической операции?
mironov_orig 12.08.2011 18:32 #
Маэстро подтянулся ☺ лучше б ты написал статью )
Говорил же, что черновик, на мой взгляд в основном для аривметических операций лучше let, но если нужно inline что-то посчитать-подставить, то $(()) безусловно лучше.
ananas 12.08.2011 19:43 #
для статьи вдохновение надо. а я в отпуске
ananas 12.08.2011 10:00 #
> 1. внутри [[ ]] не действует «разбиение на слова» (word spliting) ⇒ мы спокойно можем использовать переменные без «заковычивания», если в строке есть пробел, ничего страшного не случится.

а если внутри == или && ?
mironov_orig 12.08.2011 18:34 #
Всё равно всё будет путём и эта часть воспримится именно как строка для сравнения.
ananas 12.08.2011 19:41 #
не знал. 10х, учту на будущее