Вот уже как неделю английская версия the art of command line висит в секции trending на Github. Для себя я нашел этот материал невероятно полезным и решил помочь сообществу его переводом на русский язык. В переводе наверняка есть несколько недоработок, поэтому милости прошу слать пулл-реквесты мне сюда или автору оригинальной работы Joshua Levy вот сюда. (Если PR отправите мне, то я после того, как пересмотрю изменения отправлю их в мастер-бранч Джоша). Отдельное спасибо jtraub за помощь и исправление опечаток.
(тсс, в маркдауне читабельнее)
Описание
Основное:
- Данная публикация предназначена как для новичков, так и для опытных людей. Цели: объемность (собрать все важные аспекты использования командной строки), практичность (давать конкретные примеры для самых частых юзкейсов) и краткость (не стоит углубляться в неочевидные вещи, о которых можно почитать в другом месте).
- Этот документ написан для пользователей Linux, с единственным исключеним – секцией “MacOS only“. Все остальное подходит и может быть установлено под все UNIX/MacOS системы (и даже Cygwin).
- Фокусируемся на интерактивном Баше, но многие вещи так же могут быть использованы с другими шеллами; и в общем применимы к Баш-скриптингу
- Эта инструкция включает в себя стандартные Unix команды и те, для которых нужно устанавливать сторонние пакеты – они настолько полезны, что стоят того, чтобы их установили
Заметки:
- Для того, чтобы руководство оставалось одностраничным, вся информация вставлена прямо сюда. Вы достаточно умные для того, чтобы самостоятельно изучить вопрос более детально в другом месте. Используйте apt-get/yum/dnf/pacman/pip/brew (в зависимости от вашей системы управления пакетами) для установки новых программ.
- На Explainshell можно найти простое и детальное объясение того, что такое команды, флаги, пайпы и т.д.
Основы
- Выучите основы Баша. Просто возьмите и напечатайте man bash в терминале и хотя бы просмотрите его; он довольно просто читается и он не очень большой. Другие шеллы тоже могут быть хороши, но Баш – мощная программа и Баш всегда под рукой (использование исключительно zsh, fish и т.д., которые наверняка круто выглядят на Вашем ноуте во многом Вас ограничивает, например Вы не сможете использовать возможности этих шеллов на уже существующем сервере).
- Выучите хотя бы один консольный редактор текста. Идеально Vim (vi), ведь у него нет конкурентов, когда вам нужно быстренько что-то подправить (даже если Вы постоянно сидите на Emacs/какой-нибудь тяжелой IDE или на каком-нибудь модном хипстерском редакторе)
- Знайте как читать документацию через man (для любознательных – man man; man по углам документа в скобках добавляет номер, например 1 – для обычных команд, 5 – для файлов, конвенций, 8 – для административных команд). Ищите мануалы через apropos, и помните, что некоторые команды – не бинарники, а встроенные команды Баша, и помощь по ним можно получить через help и help -d.
- Узнайте о том, как перенаправлять ввод и вывод через > и < и пайпы |. Помните, что > – переписывает выходной файл, а >> добавляет к нему. Узнайте побольше про stdout and stderr.
- Узнайте побольше про file glob expansion with * (and perhaps ? and {…}), кавычки а так же разницу между двойными " и одинарными ' кавычками. (Больше о расширении переменных читайте ниже)
- Будьте знакомы с работой с процессами в Bash: &, ctrl-z, ctrl-c, jobs, fg, bg, kill, и т.д.
- Знайте ssh, и основы безпарольной аунтефикации через ssh-agent, ssh-add, и т.д.
- Основы работы с файлами: ls и ls -l (в частности узнайте, что значит каждый столбец в ls -l), less, head, tail и tail -f (или даже лучше, less +F), ln и ln -s (узнайте разницу между символьными ссылками и жесткими ссылками и почему жесткие ссылки лучше), chown, chmod, du (для быстрой сводки по использованию диска: du -hk *). Для менеджмента файловой системы, df, mount, fdisk, mkfs, lsblk.
- Основы работы с сетью: ip или ifconfig, dig.
- Хорошо знайте регулярные выражения и разные флаги к grep/egrep. Такие флаги как -i, -o, -A, и -B стоит знать.
- Обучитесь использованию системами управления пакетами apt-get, yum, dnf или pacman (в зависимости от дистрибутива). Знайте как искать и устанавливать пакеты и обязательно имейте установленым pip для установки командных утилит, написаных на Python (некоторые из тех, что вы найдете ниже легче всего установить через pip)
Ежедневное использование
- Используйте таб в Баше для автокомплита аргументов к командам и ctrl-r для поиска по истории командной строки
- Используйте ctrl-w в Баше для того, чтобы удалить последнее слово в команде; ctrl-u для того, что бы удалить команду полностью. Используйте alt-b и alt-f для того, чтобы бегать между словами команды, ctrl-k для того, чтобы прыгнуть к концу строки, ctrl-l для того, чтобы очистить экран. Гляньте на man readline чтобы узнать о всех шорткатах Баша. Их много! Например, alt-. бежит по предыдущим аргументам команды, а alt-* расширяет глоб.??
- Если Вам нравятся шорткаты Вима сделайте set -o vi.
- Для того, чтобы посмотреть историю введите history. Так же существует множество аббривиатур, например !$ – последний аргумент, !! – последняя команда, хотя эти аббревиатуры часто заменяются шорткатами ctrl-r и alt-..
- Для того, чтобы прыгнуть к последней рабочей директории – cd -
- Если Вы написали команду наполовину и вдруг передумали нажмите alt-# для того, чтобы добавить # к началу и отправьте команду как комментарий. Потом вы сможете вернуться к ней через историю.
- Не забывайте использовать xargs (или parallel). Это очень мощная штука. Обратите внимание, что Вы можете контролировать количество команд на каждую строку, а так же параллельность. Если Вы не уверены, что делаете что-то правильно, начните с xargs echo. Еще -I{} – полезная штука. Примеры:
find . -name <span class="hljs-string">'*.py'</span> | xargs grep some_function
cat hosts | xargs -I{} ssh root@{} hostname
- pstree -p – полезный тип вывода дерева процессов.
- Используйте pgrep или pkill для того чтобы находить/слать сигналы к процессам по имени (-f помогает).
- Знайте разные сигналы, которые можно слать процессам. Например, чтобы приостановить процесс используйте kill -STOP [pid]. Для полного списка посмотрите man 7 signal.
- Используйте nohup или disown для того, чтобы процесс в фоне выполнялся бесконечно.
- Узнайте какие процессы слушают порты через netstat -lntp или ss -plat (для TCP; добавьте -u для UDP).
- Так же lsof для того, чтобы посмотреть открытые сокеты и файлы.
- Используйте alias для того, чтобы алиасить часто используемые команды. Например, alias ll='ls -latr' создаст новый алиас ll.
- В Баш скритах используйте set -x для того, чтобы дебажить аутпут. Используйте строгие режимы везде, где возможно. Используйте set -e для того, чтобы прекращать выполнение при ошибках. Используйте set -o pipefail для того, чтобы строго относится к ошибкам (это немного глубокая тема). Для более сложных скриптов так же используйте trap.
- В Баш скриптах, подоболочки (subshells) – удобный способ группировать команды. Один из самых распространенных примеров – временно передвинуться в другую рабочую директорию, вот так:
# do something in current dir
(cd /some/other/dir && other-command)
# continue in original dir
- В Баше много типов пространства переменных. Проверить, существует ли переменная – ${name:?error message}. Например, если Баш-скрипту нужен всего один аргумент просто напишите input_file=${1:?usage: $0 input_file}. Арифметическая область видимости: i=$(( (i + 1) % 5 )). Последовательности: {1..10}. Обрезка строк: ${var%suffix} и ${var#prefix}. Например, если var=foo.pdf тогда echo ${var%.pdf}.txt выведет foo.txt.
- Вывод любой команды можно обратить в файл через <(some command). Например, сравнение локального файла `/etc/hosts с удаленным:
diff /etc/hosts <(ssh somehost cat /etc/hosts)
- Знайте про heredoc-синтаксис в Баше, работает он вот так cat <<EOF ....
- В Баше перенаправляйте стандартный вывод, а так же стандартные ошибки вот так: some-command >logfile 2>&1. Зачастую для того, чтобы убедится, что команда не оставит открытым файл, привязав его к открытому терминалу считается хорошей практикой добавлять </dev/null.
- Используйте man ascii для хорошей ASCII таблицы, с hex и десятичными значениями. Для информации по основным кодировкам полезны: man unicode, man utf-8, и man latin1.
- Используйте screen или tmux для того, чтобы иметь несколько экранов в одном терминале, это особенно полезно, когда вы работаете с удаленным сервером, тогда Вы можете подключаться/отключаться от сессий. Более минималистичный подход для этого – использование dtach.
- В SSH полезно знать как port tunnel с флагами -L и -D (и иногда -R), например для того, чтобы зайти на сайт с удаленного сервера.
- Еще может быть полезно оптимизировать вашу SSH конфигурацию, например этот файл ~/.ssh/config содержит настройки, которые помогают избегать потерянные подключения в некоторых сетевых окружениях, используйте сжатие (которое полезно с scp через медленные подключения) и увеличьте количество каналов к одному серверу через этот конфиг вот так:
TCPKeepAlive=yes
ServerAliveInterval=15
ServerAliveCountMax=6
Compression=yes
ControlMaster auto
ControlPath /tmp/%r@%h:%p
ControlPersist yes
- Некоторые другие настройки SSH могут сильно повлиять на безопасность и должны меняться осторожно, например для конкретной подсети или конкретной машины или в домашних сетях: StrictHostKeyChecking=no, ForwardAgent=yes
- Для того, чтобы получить разрешения файла в восьмеричном виде, что полезно для конфигурации систем, но нельзя получить из ls, можно использовать что-то типа:
stat -c '%A %a %n' /etc/timezone
- Для интерактивного выделения результатов других команд используйте percol or fzf.
- Для работы с файлами, список которых дала другая команда (например Git) используйте fpp (PathPicker).
- Для того чтобы быстро поднять веб-сервер в текущей директории (и поддерикториях), который доступен для всех в вашей сети используйте:
python -m SimpleHTTPServer 7777 (for port 7777 and Python 2) and python -m http.server 7777 (for port 7777 and Python 3). - Для того, чтобы выполнить определенную команду с привилегиями, используйте sudo (для рута) и sudo -u (для другого пользователя). Используйте su или sudo bash для того чтобы запустить шелл от имени этого пользователя. Используйте su - для того, чтобы симулировать свежий логин от рута или другого пользователя.
Процессинг файлов и информации
- Для того, чтобы найти файл в текущей директории сделайте find. -iname '*something*'. Для того, чтобы искать файл по всей системе используйте locate something (но не забывайте, что updatedb мог еще не проиндексировать недавно созданные файлы).
- Для основого поиска по содержимому файлов (более сложному, чем grep -r) используйте ag.
- Для конвертации HTML в текст: lynx -dump -stdin
- Для конвертации разных типов разметки (HTML, маркдаун и т.д.) попробуйте pandoc.
- Если нужно работать с XML, есть старая но хорошая утилита – xmlstarlet.
- Для работы с JSON используйте jq.
- Для работы с Excel и CSV-файлами используйте csvkit (программа предоставляет команды in2csv, csvcut, csvjoin, csvgrep и т.д.)
- Для работы с Amazon S3 удобно работать с s3cmd и s4cmd (последний работает быстрее). Для остальных сервисов Амазона используйте стандартный aws.
- Знайте про sort и uniq, включая флаги -u и -d, смотрите примеры ниже. Еще гляньте на comm.
- Знайте про cut, paste, и join для работы с текстовыми файлами. Многие люди используют cut забыв про join.
- Знайте о wc: для подсчета переводов строк (-l), для символов – (-m), для слов – words (-w), для байтового подсчета – (-c).
- Знайте про tee, для копирования в файл из stdin и stdout, что-то типа ls -al | tee file.txt.
- Не забывайте, что Ваша локаль влияет на многие команды, включая порядки сортировки, сравнение и производительность. Многие дистрибутивы Linux автоматически выставляют LANG или любую другую переменную в подходящую для Вашего региона. Из-за этого результаты функций сортировки могут работать непредсказуемо. Рутины i18n могут значительно снизить производительность сортировок. В некоторых случаях можно полностью этого избегать (за исключением редких случаев), сортируя традиционно побайтово, для этого export LC_ALL=C
- Знайте основы awk и sed для простых манипуляций с данными. Например, чтобы получить сумму всех чисел, которые находятся в третьей колонки текстового файла можно использовать awk '{ x += $3 } END { print x }'. Скорее всего, это раза в 3 быстрее и раза в 3 проще чем делать это в Питоне.
- Чтобы заменить все нахождения подстроки в одном или нескольких файлах:
perl -pi.bak -e 's/old-string/new-string/g' my-files-*.txt
Для того, чтобы переименовать сразу много файлов по шаблону, используйте rename. Для сложных переименований может помочь repren:
# Восстановить бекапы из foo.bak в foo:
rename 's/\.bak$//' *.bak
# Полное переименование имен файлов, папок и содержимого файлов из foo в bar.
repren --full --preserve-case --from foo --to bar .
- Используйте shuf для того, чтобы перемешать строки или выбрать случайную строчку из файла.
- Знайте флаги sortа. Для чисел используйте -n, для работы с человекочитаемыми числами используйте -h (например du -h). Знайте как работают ключи (-t и -k). В частности, не забывайте что вам нужно писать -k1,1 для того, чтобы отсортировать только первое поле; -k1 значит сортировка учитывая всю строчку. Так же стабильная сортировка может быть полезной (sort -s). Например для того, чтобы отсортировать самое важное по второму полю, а второстепенное по первому можно использовать sort -k1,1 | sort -s -k2,2`.
- Если вам когда-нибудь придется написать код таба в терминале, например для сортировки по табу с флагом -t, используйте шорткат ctrl-v [Tab] или напишите $'\t'. Последнее лучше, потому что его можно скопировать.
- Стандартные инструменты для патчинга исходников это diff и patch. Так же посмотрите на diffstat для просмотра статистики диффа. diff -r работает для по всей директории. Используйте diff -r tree1 tree2 | diffstat для полной сводки изменений.
- Для бинарников используйте hd для простых hex-дампом и bvi для двоичного изменения бинарников.
- strings (в связке grep или чем-то похожем) помогает найти строки в бинарниках.
- Для того, чтобы посмотреть разницу в бинарниках (дельта кодирование) используйте xdelta3.
- Для конвертирования кодировок используйте iconv. Для более сложных задач – uconv, он поддерживает некоторые сложные фичи Юникода. Например эта команда переводит строки из файла в нижний регистр и убирает ударения (кои бывают, например, в Испанском)
uconv -f utf-8 -t utf-8 -x '::Any-Lower; ::Any-NFD; [:Nonspacing Mark:] >; ::Any-NFC; ' < input.txt > output.txt
- Для того, чтобы разбить файл на куски используйте split (разбивает на куски по размеру), или csplit (по шаблону или регулярному выражению)
- Используйте zless, zmore, zcat, и zgrep для работы с сжатыми файлами.
Системный дебаггинг
- Дле веб-дебаггинга используйте curl и curl -I, или их альтернативу wget. Так же есть более современные альтернативы, типа httpie.
- Чтобы получить информацию о диске/CPU/сети используйте iostat, netstat, top (или лучшую альтернативу htop) и особенно dstat. Хороший старт для того, чтобы понимать что происходит в системе.
- Для более детальной информации используйте glances. Эта программа показывает сразу несколько разных статистик в одном окне терминале. Полезно, когда следите за сразу несколькими системами.
- Для того, чтобы следить за памятью научитесь понимать free и vmstat. В частности не забывайте, что кешированые значения (“cached” value) – это память, которую держит ядро и эти значения являются частью free.
- Дебаггинг Джавы – совсем другая рыбка, но некоторые манипуляции над виртуальной машиной Оракла или любой другой позволит вам использовать делать kill -3 <pid> и трассировать сводки стека и хипа (включая детали работы сборщика мусора, которые бывают очень полезными), и их можно дампнуть в stderr или логи.
- Используйте mtr для лучшей трассировки, чтобы находить проблемы сети.
- Для того, чтобы узнать почему диск полностью забит используйте ncdu, это сохраняет время по сравнению с тем же du -sh *.
- Для того, чтобы узнать какой сокет или процесс использует интернет используйте iftop или nethogs.
- ab, которая поставляется вместе в Апачем полезна для быстрой нетщательной проверки производительности веб-сервера. Для более серьезного лоад-тестинга используйте siege.
- Для более серьезного дебаггинга сетей используйте wireshark, tshark, и ngrep.
- Знайте про strace и ltrace. Эти команды могут быть полезны, если программа падает или висит и вы не знаете почему, или если вы хотите протестировать производительность программы. Не забывайте про возможность дебаггинга (-c) и возможностью прицепиться к процессу (-p).
- Не забывайте про ldd для проверки системных библиотек.
- Знайте как прицепиться к бегущему процессу через gdb и получить трассировку стека.
- Используйте /proc. Иногда он невероятно полезен для дебага запущенных программ. Примеры: /proc/cpuinfo, /proc/xxx/cwd, /proc/xxx/exe, /proc/xxx/fd/, /proc/xxx/smaps.
- Когда дебажете что-то, что сломалось в прошлом используйте sar – бывает очень полезно. Показывает историю CPU, памяти, сети и т.д.
- Для анализа более глубоких систем и производительности посмотрите на stap (SystemTap), perf, и sysdig.
- Узнайте какая у вас операционка через uname or uname -a (основная Unix-информация/информация о ядре) или lsb_release -a (информация о дистрибутиве).
- Используйте dmesg когда что-то ведет себя совсем странно (например железо или драйвера).
В одну строчку
Давайте соберем все вместе и напишем несколько команд:
- Это довольно круто, что можно найти множественные пересечения файлов, соединить отсортированные файлы и посмотреть разницу в нескольких файлов через sort/uniq. Это быстрый подход и работает на файлах любого размера (включая многогигабайтные файлы). (Сортировка не ограничена памятью, но возможно вам придется добавить -T если /tmp находится на небольшом логическом диске). Еще посмотрите то что было сказано выше о LC_ALL. Флаг сорта -u option не используется ниже чтобы было понятнее:
cat a b | sort | uniq > c # c is a union b
cat a b | sort | uniq -d > c # c is a intersect b
cat a b b | sort | uniq -u > c # c is set difference a - b
- Используйте grep. * для того, чтобы посмотреть содержимое всех файлов в директории, особенно послено когда у вас много конфигов типа /sys, /proc, /etc.
- Получить сумму всех чисел, которые находятся в третьей колонки текстового файла. (Скорее всего, это раза в 3 быстрее и раза в 3 проще чем делать это в Питоне):
awk '{ x += $3 } END { print x }' myfile
- Если вам нужно посмотреть размеры и даты создания древа файлов используйте:
find . -type f -ls
Это почти как рекурсивная ls -l, но более читабельно чем ls -lR:
- Используйте xargs (или parallel). Это очень мощная штука. Обратите внимание, что Вы можете контролировать количество команд на каждую строку, а так же параллельность. Если Вы не уверены, что делаете что-то правильно, начните с xargs echo. Еще -I{} – полезная штука. Примеры:
find . -name '*.py' | xargs grep some_function
cat hosts | xargs -I{} ssh root@{} hostname
- Давайте представим, что у нас есть какой-то текстовый файл, например лог какого-то сервера и на каких-то строках появляется значение, строки с которой нам интересны, например acct_id. Давайте подсчитаем сколько таких запросов в нашем логе:
cat access.log | egrep -o 'acct_id=[0-9]+' | cut -d= -f2 | sort | uniq -c | sort -rn
- Запустите этот скрипт чтобы получить случайный совет из этой инструкции:
function taocl() {
curl -s http://ift.tt/1Hj3oZ0 |
pandoc -f markdown -t html |
xmlstarlet fo --html --dropdtd |
xmlstarlet sel -t -v "(html/body/ul/li[count(p)>0])[$RANDOM mod last()+1]" |
xmlstarlet unesc | fmt -80
}
Сложно, но полезно
- expr: для выполнения арифметических и булевых операций, а так же регулярных выражений
- m4: простенький макро-процессор
- yes: вывод строки в бесконечном цикле
- cal: классный календарь
- env: для того, чтобы выполнить команду (полезно в Bash-скриптах)
- printenv: print out environment variables (useful in debugging and scripts)
- look: найти английские слова (или строки) в файле
- cut:, paste и join: манипуляция данными
- fmt: форматировка параграфов в тексте
- pr: отформатировать текст в страницы/колонки
- fold: (обернуть) ограничить длину строк в файле
- column: форматировать текст в колонки или таблицы
- expand: и unexpand: конвертация между табами и пробелами
- nl: добавить номера строк
- seq: вывести числа
- bc: калькулятор
- factor: возвести числа в степень
- gpg: зашифровать и подписать файлы
- toe: таблица терминалов terminfo с описанием
- nc: дебаггинг сети и передачи данных
- socat: переключатель сокетов и перенаправление tcp-портов (похоже на netcat)
- slurm: визуализация трафика сети
- dd: перенос информации между файлами и девайсами
- file: узнать тип файла
- tree: показать директории и сабдиректории в виде дерева; как ls, но рекурсивно
- stat: информация о файле
- tac: вывести файл наоборот (ласипан)
- shuf: случайная выборка строк из файла
- comm: построчно сравнить отсортированные файлы
- pv: мониторинг прогресса прохождения информации через пайп
- hd: и bvi: дамп и редактирование бинарников
- strings: найти текст в бинарникх
- tr: манипуляция с char (символьным типом)
- iconv: и uconv: конвертация кодировок
- split: и csplit: разбить файлы
- sponge: прочитать весь инпут перед тем, как его записать, полезно когда читаешь из того же файла, куда записываешь, например вот так: grep -v something some-file | sponge some-file
- units: конвертер, метры в келометры, версты в пяди (смотрите /usr/share/units/definitions.units)
- 7z: архиватор с высокой степенью сжатия
- ldd: показывает зависимости программы от системных библиотек
- nm: получаем названия всех функций, которые определены в .o или .a
- ab: бенчмаркинг веб-серверов
- strace: дебаг системных вызовов
- mtr: лучшей трассировка для дебаггинга сети
- cssh: несколько терминалов в одном UI
- rsync: синхронизация файлов и папок через SSH
- wireshark: и tshark: перехват пакетов и дебаг сети
- ngrep: grep для слоя сети (network layer)
- host: и dig: узнать DNS
- lsof: процессинг дискрипторов и информация о сокетах
- dstat: полезная статистика системы
- glances: высокоуровневая, многосистемная статистика
- iostat: статистика CPU и использования жесткого диска
- htop: улучшенная версия top
- last: история логинов в систему
- w: под каким пользователем вы сидите
- id: информация о пользователе/группе
- sar: история системной статистики
- iftop: или nethogs: использование сети конкретным сокетом или процессом
- ss: статистика сокетов
- dmesg: ошибки бута и ошибки системы
- hdparm: манипуляция с SATA/ATA
- lsb_release: информация о дистрибутиве Linux
- lsblk: cписок блочных устройств компьютера: древо ваших дисков и логических дисков
- lshw:, lscpu, lspci, lsusb, dmidecode: информация о железе включая, CPU, BIOS, RAID, графику, девайсы, и т.д.
- fortune:, ddate, и sl: хм, не знаю будут ли вам “полезны” веселые цитатки и поезда, пересекающие ваш терминал :)
MacOS only
Некоторые вещи релевантны только для Мака.
- Системы управлением пакетами – brew (Homebrew) и port (MacPorts). Они могут быть использованы для того, чтобы установить большинство програм, упомянутых в этом документе.
- Копируйте аутпут консольных программ в десктопные через pbcopy, и вставляйте инпут через pbpaste.
- Для того, чтобы открыть файл или десктопную программу типа Finder используйте open, вот так open -a /Applications/Whatever.app.
- Spotlight: Ищите файлы в консоле через mdfind и смотрите метадату (например EXIF информацию фотографий) через mdls.
- Не забывайте, что MacOS основан на BSD Uni и многие команды (например ps, ls, tail, awk, sed) имеют некоторые небольшие различия с линуксовыми. Это обусловлено влянием UNIX System V и GNU Tools. Разницу можно заметить увидив заголовок “BSD General Commands Manual.” к манам программ. В некоторых случаях, на Мак можно поставить GNU-версии программ, например gawk и gsed. Когда пишите кроссплатформенные Bash-скрипты, старайтесь избегать команды, которые могут различаться (например, лучше используйте Python или perl), или тщательно все тестируйте.
Больше информации по теме
- awesome-shell: Дополняемый список инструментов и ресурсов для командной строки.
- Strict mode Для того, чтобы писать шелл-скрипты лучше.
Дисклеймер
За небольшим исключением, весь код написан так, что другие его смогут прочитать.
Кому много дано, с того много и спрашивается. Тот факт, что что-то может быть написано в Баше, вовсе не означает что оно должно быть там написано. ;)
Лицензия
Оригинальная работа и перевод на русский язык распространяется под лицензией Creative Commons Attribution-ShareAlike 4.0 International License.
This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий