Skip to content

Instantly share code, notes, and snippets.

@eteresh
Last active December 6, 2017 12:04
Show Gist options
  • Save eteresh/9cfb7bf82ee58c7555d6e0d147dc6f35 to your computer and use it in GitHub Desktop.
Save eteresh/9cfb7bf82ee58c7555d6e0d147dc6f35 to your computer and use it in GitHub Desktop.

Краткое руководство по работе в командной строке с помощью интерпретатора bash

Полезные ссылки по написанию bash-скриптов

Глобальные переменные в bash

Задать значение глобальной переменной

export LD_LIBRARY_PATH="/usr/local/lib"
echo ${LD_LIBRARY_PATH}

Добавить новый путь в глобальную переменную

Часто бывает нужно добавить в переменную новый путь так, чтобы он отделялся от существующих путей символом двоеточия. Сделать это можно как-то так:

export LD_LIBRARY_PATH="/usr/local/lib:${LD_LIBRARY_PATH}"
echo ${LD_LIBRARY_PATH}

Если изначально в переменной LD_LIBRARY_PATH был записан путь /usr/lib, то после добавления нового пути в переменной LD_LIBRARY_PATH будет храниться строка: /usr/local/lib:/usr/lib. Но если изначально переменная LD_LIBRARY_PATH была пустой, то после добавления в ней будет ненужное двоеточие:/usr/local/lib:. Чтобы этого избежать, добавлять пути лучше такой командой:

export LD_LIBRARY_PATH="${LD_LIBRARY_PATH+"${LD_LIBRARY_PATH}:"}/usr/local/lib"
echo ${LD_LIBRARY_PATH}

Команда source

Данная команда выполняет в текущем интерпретаторе команды из bash-скрипта. Первый аргумент этой команды -- имя bash-скрипта, все последующие аргументы -- это аргументы bash-скрипта.

source my_script.sh

Например, для активации окружения анаконды с именем "py27" нужно выполнить

source activate py27

В этом случае команда source выполняет скрипт activate (путь к которому прописан в переменной PATH) с аргументом py27.

У команды source есть алиас -- . (точка). Поэтому, предыдущую команду можно было бы выполнить так:

. activate py27

Если вы предполагаете, что ваш код будут читать другие люди, то лучше не использовать точку, а писать явно команду source.

Если же мы просто запустим скрипт:

./my_script.sh

или так:

bash ./my_script.sh

то этот скрипт выполнится в новом интерпретаторе.

Заголовок bash-скрипта

Чтобы bash-скрипт завершался, если где-то произошла ошибка, добавьте в заголовок скрипта:

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

Чтение аргументов bash-скрипта

usage="script expects 2 required arguments and three optional arguments which have to be placed after required arguments.\n"
usage="${usage}Usage example:\n./$(basename "$0") input_filename output_filename [-e 'utf-8'] [-d ','] [-v]"
n_required_arguments=2
if [ "$#" -lt ${n_required_arguments} ]; then
    echo -e ${usage}
    exit
fi
input_filename=$1
output_filename=$2
OPTIND=$(( ${n_required_arguments} + 1 ))
delimiter=","
encoding="utf-8"
verbose=false
while getopts "d:e:v" opt
do
    case $opt in
        d) delimiter=$OPTARG;;
        e) encoding=$OPTARG;;
        v) verbose=true;;
    esac
done

Функции в bash

function list_folder() {
    folder=$1
    ls -alh ${folder}
}
list_folder $HOME

Преобразование вывода команды

Использовать вывод одной команды как аргумент другой команды

В этом примере вывод команды ls используется как аргумент команды echo:

echo $(ls -lSh $HOME)

Использовать выход команды как файл

head <(ls -lSh $HOME)

Пример

Предположим, что мы хотим сравнить два файла с помощью команды diff. Но содержимое обоих файлов неупорядоченно, и перед сравнением файлов мы должны их отсортировать. Все можно сделать в одной строке:

diff <(sort first_file.csv) <(sort second_file.csv)

Последовательный проход по датам

start_date='2017-01-01'
end_date='2017-03-01'
current_date=${start_date}
while [ "${current_date}" \< "${end_date}" ]; do
    echo ${current_date}
    current_date=$(date --date "${current_date} + 1 day" "+%Y-%m-%d")
done

Утилиты Linux

Копирование файлов

Для копирования файлов используйте rsync и только rsync! В отличие от команды cp rsync выполняет умное копирование:

  • rsync возобновляет копирование, если оно было прервано
  • rsync не будет повторно копировать файл, если он уже скопирован

Скопировать файл можно так:

rsync -aP /source_folder/filename.csv /destination_folder/
rsync -aP user@hostname:~/source_folder/filename.csv /destination_folder/
rsync -aP -e "ssh -p 2200" user@hostname:~/source_folder/filename.csv /destination_folder/

В последней команде указана опция ssh-соединения, а именно порт 2200.

Отобразить небольшую часть файла

Отобразить первые/последние 20 строк файла можно следующими командами:

head -n 20 data.csv
tail -n 20 data.csv

Игнорировать первую строку файла:

tail -n +2 data.csv

Игнорировать последнюю строку файла:

head -n -1 data.csv

Создать, распаковать архив

tar czvf archive.tar.gz ./folder
tar xzvf archive.tar.gz

Обратите внимание, что порядок опций команды tar важен! После опции f должно идти имя архива. Если опцию f поменять местами, например, с опцией v:

tar czfv archive.tar.gz ./folder

то tar постарается создать архив с именем v и добавить в него файл archive.tar.gz и директорию folder. А это совсем не то, что мы хотим. Если вы чувствуете себя неуверенно при работе с опциями команды tar, то можете писать опции полностью (быстро надоедает). Например так:

tar --create --gzip --verbose --file archive.tar.gz ./folder

Кроме сжатия алгоритмом gzip можно использовать алгоритм bzip2 (опция --bzip2) и алгоритм lzma (опция --lzma ). Оба эти алгоритма, как правило, сжимают в разы лучше, чем gzip.

Не забывайте указывать расширение архива, соответствующее алгоритму сжатия (tar.bz2 для bzip2 и tar.lzma для lzma).

Параллельный запуск скриптов

Предположим, что у нас есть скрипт analyzer.py, который принимает 3 параметра из командной строки:

  • имя файла
  • число групп точек
  • индекс группы

Тогда мы можем обработать все точки в файле, запустив скрипт параллельно с помощью команды xargs:

n_cpu=8
n_batches=100
batch_size=100000
seq 0 $(( ${n_batches} - 1))| xargs -P ${n_cpu} -n 1 analyzer.py input_data.csv ${batch_size}

Поиск файлов

Найти все файлы с расширением ".sh", находящиеся в текущей директории, или в поддиректориях, можно командой:

find ./ -name "*.sh"

Команда find может применить любую команду к каждому найденному файлу. Например, вывести размер всех найденных файлов можно командой:

find ./ -name "*.sh" -exec stat -c "%s %n" {} \;

Поиск текста по файлам

Для поиска текста можно использовать команды grep и egrep.

grep -n "some text" -r ./folder
egrep -n "some text" -r ./folder

где опция -n означает, что нужно напечатать номер строки, в которой нашелся заданный текст; опция -r задает рекурсивный поиск.

Поиск процессов

Команда pgrep позволяет найти процесс по шаблону имени. Например, найти все запущенные bash-интерпретаторы можно командой:

pgrep "bash" -a

Часто возникает такая ситуация: кто-то запустил программу, занимающую все ресурсы компьютера, и мы должны подождать, пока эта программа завершится, и потом запустить свой скрипт. Сделать это можно с помощью такого bash-скрипта:

while [[ $(pgrep -f some_program) ]]; do
    sleep 200
done
./my_script.py data.csv

Данный скрипт каждые 200 секунд будет проверять, выполняется ли еще программа с именем some_program. Если нет, то запустится команда ./my_script.py data.csv

Вырезать столбцы

Если у нас есть файл с несколькими столбцами, разделенными пробелом, и мы хотим выбрать 1-ый, 3-ий и 5-ый стобец, то мы можем выполнить:

cut -d" " -f1,3,5 data.csv

sort

Чтобы отсортировать содержимое файла по определенному (например, по 3-ему) столбцу:

sort -k3 data.csv
sort -k3 data.csv --numeric-sort
sort -k3 data.csv --reverse

где опция --numeric-sort означает сравнивать значения в столбце как числа (иначе значения будут сравниваться как строки). Я сталкивался с глюками при сортировке чисел, поэтому работу этой команды стоит проверять.

join

Комадна join объединяет два файла также, как и соответствующая команда языка SQL. Оба файла должны быть отсортированны по полю, по которому мы хотим их объединить. Объединить два несортированных файла (first_data.csv и second_data.csv) по 4-ому полю можно так:

sort -k4 first_data.csv > first_data_sorted.csv
sort -k4 second_data.csv > second_data_sorted.csv
join -j 4 first_data_sorted.csv second_data_sorted.csv

Или одной командой, используя выход команды sort как файл:

join -j 4 <(sort -k4 first_data.csv) <(sort -k4 second_data.csv)

sed

С помощью команды sed удобно выполнять замены в файле. Заменяемые строки можно задавать регулярным выражением. Например, если в файле data.csv для разделения полей используется запятая, а мы хотим, чтобы поля разделялись точкой с запятой, то мы можем выполнить:

sed -e "s/,/;/g" data.csv > data_semicolon_separated.csv

awk

С помощью команды awk удобно делать простые преобразования на данными.

Например, подсчитать сумму чисел во 2-ом столбце файла data.csv, если столбцы разделены запятой, можно командой:

cat data.csv |awk 'BEGIN{FS = ","} {res=res+$2} END{print res;}'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment