Опыт просветления: bash и Python

Не один год работая с Python и bash я, главным образом, использовал

  • первый — для написания программ,
  • второй — для выполнения команд ОС.

При этом моя работа в интерактивном режиме Python сводилась к маленьким исследованиям того, как работает некий фрагмент кода. А в командной строке bash я не пользовался конструкциями, присущими языкам программирования, такими, как if или for, ограничиваясь запуском отдельных команд.

Но в один прекрасный день 1 час параллельных импровизаций в bash и Python позволили на опыте (а не теоретически) узнать, что

  • bash — язык программирования, а не только интерпретатор командной строки, и
  • python — полноценная интерактивная рабочая среда!

Желающим получить аналогичное просветление предлагаю поимпровизировать на следующие темы в двух средах. Действительно, все познается в сравнении.

hello world

$ echo Hello world
Hello world
>>> print 'Hello world'
Hello world

Запуск скриптов

Создадим shell и Python скрипты:

$ cat > script.sh
echo Hello world
$ cat > script.py
print 'Hello world'

Запустим их:

$ bash script.sh
Hello world
$ python script.py
Hello world

Переменные

$ x=15
$ echo $x
15
>>> x=15
>>> print x
15

Выражения

$ echo x + 3 = $[ x + 3 ]
x + 3 = 18
$ echo 14 o'clock is $(( 14%12 )) PM
14 o'clock is 2 PM
>>> print 'x + 3 = ' + str(x + 3)
x + 3 = 18
>>> print "14 o'clock is %d PM" % (14 % 12)
14 o'clock is 2 PM

Функции

$ hi() {
>     echo hello
> }
$ hi
hello
$ aloha() {
>     echo Aloha $1
> }
$ aloha
Aloha
$ aloha Andrey
Aloha Andrey
>>> def hi():
...     print 'hello'
...
>>> hi()
hello
>>> def aloha(name=''):
...     print 'Aloha', name
...
>>> aloha()
Aloha
>>> aloha('Andrey')
Aloha Andrey

Библиотеки функций

Создадим библиотеки функций (с одной функцией):

$ cat > mylib.sh
helloworld() {
    echo Hello, world!
}
^D

$ cat > mylib.py
def helloworld():
print ‘Hello, world!’

^D

Воспользуемся функциями из библиотек:

$ helloworld
-bash: helloworld: command not found
$ source mylib.sh
$ helloworld
Hello, world!
>>> helloworld()
Traceback (most recent call last):
  File "", line 1, in ?
NameError: name 'helloworld' is not defined
>>> from mylib import *
>>> helloworld()
Hello, world!

Переменные окружения

Для bash все переменные — переменные окружения (environment variables), а окружение и есть контекст выполнения команд bash.

$ echo My shell is $SHELL
My shell is /bin/bash
>>> import os
>>> print os.environ['SHELL']
/bin/bash

Интерактивная справка

$ man echo
...
q
$ info echo
...
q

Посмотрим справку Python для класcа list и модуля math.

>>> help('list')
...
q
>>> help('math')
...
q

Выполнение команд ОС

$ echo Hello world
Hello world
$ echo $?
0
$ ls nosuchfile
ls: nosuchfile: No such file or directory
$ echo $?
2
>>> import os
>>> exitcode = os.system('echo Hello world')
Hello world
>>> print exitcode
0
>>> print os.system('ls nosuchfile')
ls: nosuchfile: No such file or directory
512

Условное выполнение

$ if [ 0 ]
> then
>     echo Truth
> else
>     echo False
> fi
Truth
>>> if 0:
...     print 'Truth'
... else:
...     print 'False'
...
False

Конструкция if в bash работает не только с [ проверкой ], но и с любой командой ОС. Поскольку в Unix программы возвращают 0, если выполнение успешно, то bash интерпретирует 0 как истину, а отличное от 0 значение — как ложь.

$ if grep -q "llo, world" mylib.sh
> then
>     echo $? : found
> else
>     echo $? : not found
> fi
0 : found

Циклы

$ for n in 1 2 3; do echo $(( n*n )); done
1
4
9
$ for x in {3..5} {k..m}; do echo $x; done
3
4
5
k
l
m
$ for i in `seq 3` `seq 10 12`
> do
>     echo $i
> done
1
2
3
10
11
12
>>> for n in 1, 2, 3:
...     print n*n
...
1
4
9
>>> for x in range(3, 6) + ['k', 'l', 'm']:
...     print x
...
3
4
5
k
l
m
>>> for i in range(1, 4) + range(10, 13):
...     print i
...
1
2
3
10
11
12
$ for file in `ls`; do echo $file; done
a
abc.txt
...
words
writelog

$ for file in [qf]*; do ls -l $file; done
-rw-rw-r— 1 ay ay 15 Aug 22 11:10 file1.txt
-rw-rw-r— 1 ay ay 28 Aug 22 11:12 file2.txt
-rw-rw-r— 1 ay ay 0 Apr 29 11:40 q.txt
-rw-rw-r— 1 ay ay 13 Jun 21 16:54 qwerty
-rw-rw-r— 1 ay ay 0 Apr 29 11:40 qwerty.txt

>>> import glob
>>> print glob.glob('*')
['ls1.txt', 'hello', ..., 'params.sh', 'writelog']

>>> print glob.glob(‘[qf]*’)
[‘file2.txt’, ‘qwerty.txt’, ‘q.txt’, ‘file1.txt&# 39;, ‘qwerty’]

Чтение стандартного входного потока

$ read answer
yes
$ echo $answer
yes
$ read -p "your name? " name
your name? Andrey
$ echo $name
Andrey
>>> x = raw_input()
qwerty
>>> print x
qwerty
>>> name = raw_input("your name? ")
your name? Andrey
>>> print name
Andrey

В bash, если переменная для вводимого значения явно не задана, значение сохраняется в специальной переменной REPLY:

$ read
qwerty
$ echo $REPLY
qwerty

Запись и чтение файла

$ cat > file1.txt
hello
good bye
$ cat file1.txt
hello
good bye
>>> with open('file2.txt', 'w') as f:
...     f.write("""salut
... see you later
... mata ne""")
...
>>> with open('file2.txt') as f:
...     print f.read()
...
salut
see you later
mata ne

Конструкция with поддерживается в Python начиная с версии 2.5. Для Python версий 2.4 и более ранних необходимо явно закрывать файл, закончив работу с ним:

>>> f = open('mylib.sh', 'w')
>>> f.write("""salut
... see you later
... mata ne""")
>>> f.close()
>>> f = open('mylib.sh')
>>> print f.read()
>>> f.close()
salut
see you later
mata ne

Моему индивидуальному просветлению сильно способствовала книга Advanced Bash-Scripting Guide by Mendel Cooperздесь по-русски).

Автор: Andrei Trofimov