Русский
Русский
English
Статистика
Реклама

Terminal

Todo-лист для командной строки на Deno

23.06.2020 10:15:03 | Автор: admin

Вы уже наверняка слышали про Deno и, скорее всего, прочитали пару-тройку обзоров. В рамках своей статьи я предлагаю испытать Deno на практике, написав приложение для командной строки, попутно размышляя о преимуществах и возможных способах его применения.


Приложение для терминала позволит взглянуть на стандартную библиотеку, познакомиться с вводом и выводом без использования фреймворков.



Основные преимущества, такие как безопасность, TypeScript из коробки, встроенные инструменты, обширная стандартная библиотека, а также возможность импортировать и запускать скрипты по URL, выгодно отличают Deno от Node и Bash. С помощью Deno мы можем написать скрипт, залить на GitHub Gist и запускать при необходимости командой deno run https://path.to/script.ts, не заботясь о зависимостях и настройке окружения.


Для начала работы нужно только установить Deno одним из предложенных способов https://deno.land/#installation.


Todo для Todo


Todo-лист должен уметь:


  • добавлять, редактировать, удалять и отмечать задачи;
  • выводить список задач;
  • сохранять состояние;
  • работать в интерактивном режиме;
  • устанавливаться на компьютер.

Аргументы командной строки


Практически любая консольная утилита управляется с помощью передаваемых при запуске аргументов. В Deno они лежат массивом в Deno.args. А, к примеру, в Node мы бы использовали process.argv, в котором помимо аргументов находятся строки node и путь к скрипту.


// deno run args.ts first secondconsole.log(Deno.args) // [ "first", "second" ]

В простых случаях Deno.args будет достаточно, а в более сложных можно воспользоваться парсером из стандартной библиотеки: https://deno.land/std/flags или любым другим на выбор.


Итак, начнём


Реализуем первую команду вывод информации.


Создадим главный файл нашего приложения todo.ts. Лучше назвать файл осмысленно, так как в будущем он понадобится нам для запуска и установки.


Далее создадим файл /src/ui.ts, в котором будем хранить элементы пользовательского интерфейса. Добавим константу help, содержащую информационный текст.


export const help = `todo help - to show available commands`;

В todo.ts напишем свитч для обработки команд и выведем справку с помощью console.log().


#!/usr/bin/env deno runimport { help } from "./src/ui.ts";const [command, ...args] = Deno.args;switch (command) {  case "help":    console.log(help);}

Проверим работу команды:


deno run todo.ts help


Для того, чтобы при каждом запуске не писать deno run, добавим шебанг #!.


#!/usr/bin/env deno run

Разрешим запуск файла:


chmod +x todo.ts


Теперь для вывода справки можно использовать:


./todo.ts help


Стандартный вывод


В листинге выше мы использовали console.log(), но, мне кажется, идеологически log это инструмент разработчика, и реализовать с его помощью полноценный интерфейс не получится.


В Deno за стандартный вывод отвечает Deno.stdout, за ввод, соответственно, Deno.stdin.


/** A handle for `stdin`. */export const stdin: Reader & ReaderSync & Closer & { rid: number };/** A handle for `stdout`. */export const stdout: Writer & WriterSync & Closer & { rid: number };

И stdin, и stdout типизированы пересечением интерфейсов и объекта, содержащего rid, идентификатор ресурса.


Более подробно рассмотрим stdout и выведем Hello world! в консоль.


const textEncoder = new TextEncoder();const message: string = "Hello world!\n";const encodedMessage: Uint8Array = textEncoder.encode(message);await Deno.stdout.write(encodedMessage);

Запустим скрипт hello.ts:


deno run hello.ts


На экране появляется наш Hello world!.


export interface Writer {  write(p: Uint8Array): Promise<number>;}

Функция Deno.stdout.write принимает на вход массив байтов и возвращает количество записанных байтов. Deno.stdout.writeSync работает точно так же, только синхронно.


Строку необходимо предварительно закодировать с помощью объекта TextEncoder.
Можно не создавать TextEncoder, а импортировать функцию encode из стандартной библиотеки, которая делает то же самое, точно так же.


import { encode } from 'https://deno.land/std/encoding/utf8.ts';

Воспользуемся полученными знаниями и создадим файл terminal.ts с двумя функциями print и printLines. Последняя выводит строку и переводит каретку.


import { encode } from "https://deno.land/std/encoding/utf8.ts";export async function print(message: string) {  await Deno.stdout.write(encode(message));}export async function printLines(message: string) {  await print(message + "\n");}

Заменим console.log на printLines.


#!/usr/bin/env deno runimport { help } from "./src/ui.ts";import { printLines } from "./src/Terminal.ts";const [command, ...args] = Deno.args;switch (command) {  case "help":    await printLines(help);}

Чтение и запись файла


В Deno для чтения файла используется функция Deno.readFile и её синхронный аналог Deno.readFileSync.


function Deno.readFile(path: string | URL): Promise<Uint8Array>

const decoder = new TextDecoder("utf-8");const data = await Deno.readFile("hello.txt");console.log(decoder.decode(data));

Для записи в файл соответственно используются Deno.writeFile и Deno.writeFileSync.


function Deno.writeFile(path: string | URL, data: Uint8Array, options?: WriteFileOptions): Promise<void>

const encoder = new TextEncoder();const data = encoder.encode("Hello world\n");await Deno.writeFile("hello1.txt", data);  // overwrite "hello1.txt" or create it

В стандартной библиотеке Deno содержатся функции, позволяющие упростить взаимодействие с файловой системой: https://deno.land/std/fs. Например, код выше можно заменить вызовами функций readFileStr и writeFileStr.


Создадим интерфейс Task в src/Task.ts


export interface Task {   title: string;  isDone: boolean;}

и класс Todo в файле src/Todo.ts.


import {  writeJsonSync,  readJsonSync, } from "https://deno.land/std/fs/mod.ts"; import { Task } from "./Task.ts";export class Todo {  tasks: Task[] = [];  constructor(private file: string = "tasks.json") {    this.open();  }  open() {    try {      this.tasks = readJsonSync(this.file) as Task[];    } catch (e) {      console.log(e);      this.tasks = [];    }  }  save() {    writeJsonSync(this.file, this.tasks, { spaces: 2 });  }}

Как можно заметить, для чтения и записи данных я использовал специальные функции из стандартной библиотеки для работы с json-файлами.


Добавление задачи в список


Для реализации команды todo add "implement add command" добавляем в класс Todo метод add.


add(title: string) {    const task: Task = {        title,        isDone: false    };    this.tasks.push(task);    this.save();}

Так как мы добавили чтение и запись в файл, необходимо добавить права --allow-read --allow-write.


#!/usr/bin/env deno run --allow-read --allow-write import { help } from "./src/ui.ts";import { printLines } from "./src/terminal.ts";import { Todo } from "./src/Todo.ts";const [command, ...args] = Deno.args;const todo = new Todo();switch (command) {  case "help":    await printLines(help);    break;  case "add":    todo.add(args[0]);}

Вывод списка задач


В файл src/ui.ts добавляем функцию для форматирования списка задач.


import { Task } from "./Task.ts";import { green, red, yellow, bold } from "https://deno.land/std/fmt/colors.ts";export function formatTaskList(tasks: Task[]): string {  const title = `${bold("TODO LIST:")}`;  const list = tasks.map((task, index) => {    const number = yellow(index.toString());    const checkbox = task.isDone ? green("[*]") : red("[ ]");    return `${number} ${checkbox} ${task.title}`;  });  const lines = [    title,    ...list,  ];  return lines.join("\n");}

Для задания стиля и цвета текста используем функции форматирования из https://deno.land/std/fmt/colors.ts.


Добавляем метод list в класс Todo и команду ls в свитч.


async list() {  await printLines(formatTaskList(this.tasks));}

case "ls":  await todo.list();  break;

Таким же образом добавляем остальные методы и команды.


done(index: number): void {  const task = this.tasks[index];  if (task) {    task.isDone = true;    this.save();  }}undone(index: number): void {  const task = this.tasks[index];  if (task) {    task.isDone = false;    this.save();  }}edit(index: number, title: string) {  const task = this.tasks[index];  if (task) {    task.title = title;    this.save();  }}remove(index: number) {  this.tasks.splice(index);  this.save();}

case "edit": // todo edit 1 "edit second task"  await todo.edit(parseInt(args[0], 10), args[1]);  break;case "done" : // todo done 1  todo.done(parseInt(args[0], 10));  break;case "undone" : // todo undone 1  todo.undone(parseInt(args[0], 10));  break;case "remove" : // todo remove 1  todo.remove(parseInt(args[0], 10));  break;

Реализованные команды отвечают первым трём пунктам наших требований.


Работа в интерактивном режиме


Интерактивный режим подразумевает, что программа общается с пользователем, не закрываясь после выполнения команды.


Создадим в свитче кейс по умолчанию,


default:  await todo.interactive();

а в классе Todo асинхронный метод interactive.


async interactive() {  while (true) {    // show list    // read keypress    // do action  }}

Метод interactive содержит бесконечный цикл, в котором выводится пользовательский интерфейс, ожидается ответ пользователя и, в зависимости от нажатой клавиши, совершается действие.


Для управления курсором в интерактивном режиме необходимо импортировать функции из библиотеки cursor.
Добавляем права для доступа к энвайронменту, так как библиотека проверяет, в каком окружении программа запущена --allow-env.


import { clearDown, goUp, goLeft } from "https://denopkg.com/iamnathanj/cursor@v2.0.0/mod.ts";

В файле terminal.ts создадим функцию printInteractive.


export async function printInteractive(message: string) {  const lines = message.split("\n");  const numberOfLines = lines.length;  const lengthOfLastLine = lines[numberOfLines - 1].length;  await clearDown();  await print(message);  await goLeft(lengthOfLastLine);  if (numberOfLines > 1) {    await goUp(numberOfLines - 1);  }}

Она выводит текст на экран и возвращает курсор в начало. При следующем вызове всё стирается и печатается заново. Для того чтобы квадрат курсора не мешался, его можно скрыть функцией hideCursor.


Добавим в файл ui.ts константу toolbar, в которой будут перечислены доступные действия.


const toolbar = `${yellow("d")}one ${yellow("a")}dd ${yellow("e")}dit ${yellow("r")}emove`;

В функцию formatTaskList добавляем параметр showToolbar:


export function formatTaskList(tasks: Task[], showToolbar: boolean): string {// ...if (showToolbar) {  lines.push(toolbar);}

В зависимости от флага showToolbar мы будем показывать или скрывать подсказку.


Дорабатываем метод list:


async list(interactive: boolean = false) {  if (interactive) {    await printInteractive(formatTaskList(this.tasks, true));  } else {    await printLines(formatTaskList(this.tasks, false));  }}

Вывод реализован, перейдём к вводу.


Стандартный ввод


Интерфейс ввода, аналогично выводу работает с Uint8Array, поэтому для декодирования используется TextDecoder.


const textDecoder = new TextDecoder();const buffer: Uint8Array = new Uint8Array(1024);const n: number = <number>await Deno.stdin.read(buffer);const message: string = textDecoder.decode(buffer.subarray(0, n));

Создаём буфер размером, например, в 1024 байта. Передаём его в функцию Deno.stdin.read и ждём пока пользователь закончит ввод. Как только будет нажата клавиша enter, функция наполнит буфер и вернёт количество прочитанных байтов. Стоит отметить, что перевод строки \n будет в конце прочитанной последовательности. Обрезаем лишнее и декодируем полученную строку. Синхронная функция Deno.stdin.readSync работает аналогично.


Обработка клавиш


Для того чтобы получить информацию о нажатии клавиш, необходимо воспользоваться функцией Deno.setRaw. На текущий момент она доступна под флагом --unstable. setRaw и позволяет получить символы по одному, без обработки. В функцию необходимо передать идентификатор ресурса и флаг. Как только мы воспользовались этим режимом, нажатие CTRL+C перестаёт приводить к закрытию программы, и это поведение нужно реализовать самостоятельно.


Deno.setRaw(Deno.stdin.rid, true);const length = <number> await Deno.stdin.read(buffer);Deno.setRaw(Deno.stdin.rid, false);

В файл terminal.ts добавляем функцию readKeypress:


export async function readKeypress(): Promise<string> {  const buffer = new Uint8Array(1024);  Deno.setRaw(Deno.stdin.rid, true);  const length = <number> await Deno.stdin.read(buffer);  Deno.setRaw(Deno.stdin.rid, false);  return decode(buffer.subarray(0, length));}

В метод interactive добавляем возможность выйти из приложения.


async interactive() {  while (true) {    await this.list(true);    const key = await readKeypress();    if (key == "\u0003") { // ctrl-c      Deno.exit();    }  }}

Не отходя далеко от ввода текста, реализуем добавление задачи в интерактивном режиме.


В файл terminal.ts добавляем 2 функции:


export async function readLine(): Promise<string> {  const buffer = new Uint8Array(1024);  const length = <number> await Deno.stdin.read(buffer);  return decode(buffer.subarray(0, length - 1));}export async function prompt(question: string): Promise<string> {  await clearDown();  await print(question);  const answer = await readLine();  await goLeft(question.length + answer.length);  await goUp(1);  return answer;}

Функция readLine позволяет прочитать одну строку, исключая последний символ, перевод строки.


Функция prompt очищает экран, печатает вопрос, читает ввод пользователя, а затем перемещает курсор в начало, аналогично функции printInteractive.


В метод interactive добавляем обработку клавиши а:


if (key === "a") {  const title = await prompt("Add task: ");  if (title) {    await this.add(title);  }}

Перемещение по списку задач


Добавим поддержку стрелок клавиатуры. Для того чтобы понимать, какой элемент сейчас выбран, добавляем свойство currentIndex в класс Todo:


  private currentIndex: number = 0;

А в функции formatTaskList активную строку выделяем жирным и красим в жёлтый:


export function formatTaskList(  tasks: Task[],  showToolbar: boolean = false,  activeIndex?: number,): string {  const title = `${bold("TODO LIST:")}`;  const list = tasks.map((task, index) => {    // ...    const isActive = activeIndex === index;    const title = isActive ? bold(yellow(task.title)) : task.title;    return `${number} ${checkbox} ${title}`;  });  // ...}

В Todo создаём методы, которые будут изменять currentIndex:


up() {  this.currentIndex = this.currentIndex === 0    ? this.tasks.length - 1    : this.currentIndex - 1;}down() {  this.currentIndex = this.currentIndex === this.tasks.length - 1    ? 0    : this.currentIndex + 1;}

В метод interactive добавляем обработку стрелок:


if (key === "\u001B\u005B\u0041" || key === "\u001B\u005B\u0044") { // вверх или влево  this.up();} else if (key === "\u001B\u005B\u0042" || key === "\u001B\u005B\u0043") { // вниз или вправо  this.down();}

Аналогичным образом добавляем возможность отмечать выполненные задачи:


toggle(index: number = this.currentIndex) {  const task = this.tasks[index];  if (task) {    task.isDone = !task.isDone;    this.save();  }}

if (key === "d" || key === " ") {    this.toggle();} else if ('0' <= key && key <= '9') {    this.toggle(parseInt(key, 10));} 

Дорабатываем удаление:


remove(index: number = this.currentIndex) {  if (index === this.tasks.length - 1) {    this.up();  }  this.tasks.splice(index, 1);  this.save();}

if (key === "r") {    this.remove();} 

И, наконец, редактирование:


if (key === "e") {  if (!this.tasks[this.currentIndex]) {    return;  }  const title = await prompt("Edit task (" + this.tasks[this.currentIndex].title + "): ");  if (title) {    this.edit(this.currentIndex, title);  }}

Проверяем работу приложения:


deno run --allow-read --allow-write --allow-env --unstable ./todo.ts

или


./todo.ts

Установка


Для того что бы наша программа была доступна в терминале, воспользуемся замечательной командой deno install.


deno install --allow-read --allow-write --allow-env --unstable ./todo.ts

Установка, так же как и запуск, может производиться по URL.


deno install --allow-read --allow-write --allow-env --unstable  https://raw.githubusercontent.com/dmitriytat/todo/master/todo.ts

Имя файла будет использовано как название программы по умолчанию, но с помощью опции -n/--name можно задать другое.


После чего todo можно использовать в командной строке.


Импорт зависимостей


Последняя фича, которую мы использовали, но ещё не обсудили импорт зависимостей. В отличие от Node, в Deno подход к импортированию несколько иной: предполагается, что зависимости импортируются по URL. В этом подходе, на мой взгляд, есть как достоинства, так и недостатки.


При разработке однофайловых скриптов, возможность импортировать по URL является огромным плюсом, но как только мы разработаем что-то большее, уследить за версиями станет труднее.


Для управления сложностью существует два основных решения: файл dep.ts и файл с картой импортов.


В первом случае мы делаем реэкспорт библиотек, собирая все импорты в одном месте.


export { green, red, yellow, bold } from "https://deno.land/std@0.55.0/fmt/colors.ts";export { decode, encode } from "https://deno.land/std@v0.55.0/encoding/utf8.ts";export { clearDown, goUp, goLeft } from "https://denopkg.com/iamnathanj/cursor@v2.0.0/mod.ts"; 

Во втором случае в JSON файле мы пишем alias:


import_map.json


{   "imports": {      "fmt/": "https://deno.land/std@0.55.0/fmt/"   }}

color.ts


import { red } from "fmt/colors.ts";console.log(red("hello world"));

При запуске программы указываем путь до карты и флаг --unstable, так как фича не готова для продакшена.


deno run --importmap=import_map.json --unstable color.ts.

Резюме


В целом, работой Deno я доволен. Новый инструмент позволяет не тратить время на настройку окружения и сразу приступать к написанию кода, что оказалось очень удобно.
Таким образом, я могу рекомендовать Deno для написания небольших скриптов и программ, упрощающих жизнь разработчика, но пока воздержался бы от использования Deno для создания продакшен-приложений.

Подробнее..

Перевод Что JavaScript-разработчику следует знать о Curl

28.06.2020 14:08:19 | Автор: admin


Доброго времени суток, друзья!

Представляю Вашему вниманию перевод статьи What JavaScript Developers Should Know About Curl автора Valery Karpov.

Curl это популярный инструмент командной строки, часто используемый для отправки HTTP-запросов. Curl поддерживает большое количество протоколов, однако как Node.js-разработчик вы, скорее всего, будете использовать его для отпраки http-запросов к RESTful API.

Документация curl представляет собой перечень из 383 флагов командной строки, поэтому в ней очень трудно найти то, что ищешь. В этой статье я хочу поделиться с вами некоторыми часто используемыми мной шаблонами. Для примеров будет использоваться сервис httpbin.org.

Отправка http-запроса


Для начала убедитесь в том, что у вас установлен curl, выполнив команду curl --version.

$ curl --versioncurl 7.58.0 (x86_64-pc-linux-gnu) libcurl/7.58.0 OpenSSL/1.1.1 zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3Release-Date: 2018-01-24Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL 

Для отправки запроса необходимо запустить curl url. Например, для отправки GET-запроса к https://httpbin.org/get?answer=42 следует запустить curl https://httpbin.org/get?answer=42.

$ curl https://httpbin.org/get?answer=42{  "args": {    "answer": "42"  },   "headers": {    "Accept": "*/*",     "Host": "httpbin.org",     "User-Agent": "curl/7.58.0",     "X-Amzn-Trace-Id": "Root=1-5ee8d737-b39c6a466892725bbb52b916"  },   "origin": "69.84.111.39",   "url": "https://httpbin.org/get?answer=42"}

После успешного завершения запроса curl возвращает тело http-ответа. Для того, чтобы заставить curl вернуть весь ответ, включая заголовки, используйте флаг -i.

$ curl -i https://httpbin.org/get?answer=42HTTP/2 200 date: Tue, 16 Jun 2020 14:30:57 GMTcontent-type: application/jsoncontent-length: 801server: istio-envoyaccess-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 2{  "args": {    "answer": "42"  },   "headers": {    "Accept": "*/*",     "Content-Length": "0",     "Host": "httpbin.org",     "User-Agent": "curl/7.58.0",     "X-Amzn-Trace-Id": "Root=1-5ee8d7a1-cb3954c09299eb9e0dff70a6",     "X-B3-Parentspanid": "dffc55451e64b5fc",     "X-B3-Sampled": "0",     "X-B3-Spanid": "8e233a863fb18b6c",     "X-B3-Traceid": "45bd12a9067fb5c0dffc55451e64b5fc",     "X-Envoy-External-Address": "10.100.91.201",     "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/httpbin-istio/sa/httpbin;Hash=c1ff14671b3e24ee794f9a486570abf8ccc9d622846611d3f91a322db4d480cd;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"  },   "origin": "69.84.111.39,10.100.91.201",   "url": "http://httpbin.org/get?answer=42"}

Это полный http-ответ. Заголовками ответа являются строки от date: до x-envoy-upstream-service-time:.

Загрузка файлов


Wget самый распространенный инструмент для загрузки файлов посредством командной строки. Он входит в комплект большинства диструбутивов Linux. Однако в OSX его нет.

Команда wget url аналогична команде curl -OL url. Опция это опция --remote-name, которая говорит curl сохранить тело ответа в локальном файле. Опция -L говорит curl следовать перенаправлениям.

Например, ниже представлено изображение с Unsplash, его URL https://images.unsplash.com/photo-1506812574058-fc75fa93fead.



Для загрузки этого изобюражения необходимо выполнить следующее:

$ curl -OL https://images.unsplash.com/photo-1506812574058-fc75fa93fead  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                 Dload  Upload   Total   Spent    Left  Speed100 12.1M  100 12.1M    0     0  3927k      0  0:00:03  0:00:03 --:--:-- 3927k

Опция -O говорит curl использовать строку после последнего / в качестве имени файла. В приведенном примере изображение будет сохранено в текущей директории с именем photo-1506812574058-fc75fa93fead. Для определения имени файла используйте опцию (строчная буква о).

$ curl -o miami-beach.jpg https://images.unsplash.com/photo-1506812574058-fc75fa93fead  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current                                 Dload  Upload   Total   Spent    Left  Speed100 12.1M  100 12.1M    0     0  6083k      0  0:00:02  0:00:02 --:--:-- 6083k$ ls -l miami-beach-jpg-rw-rw-r-- 1 val val 12788445 Jun 16 11:03 miami-beach.jpg

Отправка авторизованного запроса


Заголовок авторизации используется для включения в запрос данных для авторизации при обращении к RESTful API. Для добавления указанных данных необходимо использовать флаг -H. Например, если ваш ключ интерфейса (API key) my-secret-token, вы можете включить его в http-запрос следующим образом:

$ curl -H "Authorization: my-secret-token" https://httpbin.org/get{  "args": {},   "headers": {    "Accept": "*/*",     "Authorization": "my-secret-token",     "Host": "httpbin.org",     "User-Agent": "curl/7.58.0",     "X-Amzn-Trace-Id": "Root=1-5ee8e1a5-a3aa30e0765a7980b04ca4a0"  },   "origin": "69.84.111.39",   "url": "https://httpbin.org/get"}

Обратите внимание, что httpbin.org возвращает заголовки http-запроса в теле ответа в свойстве headers.

Curl также поддерживает авторизацию по-умолчанию посредством флага -u. В следующем примере мы отправляем запрос с именем пользователя user и паролем pass:

$ curl -i -u "user:pass" https://httpbin.org/basic-auth/user/passHTTP/2 200 date: Tue, 16 Jun 2020 15:18:45 GMTcontent-type: application/jsoncontent-length: 47server: istio-envoyaccess-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 1{  "authenticated": true,   "user": "user"}

Вот что происходит при отправке неправильного имени пользователя или пароля:

$ curl -i -u "user:wrongpass" https://httpbin.org/basic-auth/user/passHTTP/2 401 date: Tue, 16 Jun 2020 15:18:51 GMTcontent-length: 0server: istio-envoywww-authenticate: Basic realm="Fake Realm"access-control-allow-origin: *access-control-allow-credentials: truex-envoy-upstream-service-time: 12

Отправка POST-запроса, содержащего JSON


Флаг -X говорит curl, какой метод следует использовать: PUT, POST и т.д. По-умолчанию curl использует метод GET, поэтому писать curl -X GET не нужно.

Флаг -X часто используется совместно с флагом -d, позволяющим добавить тело запроса. В следующем примере показано как отправить POST-запрос, содержащий некоторый json:

$ curl -X POST -d '{"answer":42}' https://httpbin.org/post{  "args": {},   "data": "",   "files": {},   "form": {    "{\"answer\":42}": ""  },   "headers": {    "Accept": "*/*",     "Content-Length": "13",     "Content-Type": "application/x-www-form-urlencoded",     "Host": "httpbin.org",     "User-Agent": "curl/7.58.0",     "X-Amzn-Trace-Id": "Root=1-5ee8e3fd-8437029087be44707bd15320",     "X-B3-Parentspanid": "2a739cfc42d28236",     "X-B3-Sampled": "0",     "X-B3-Spanid": "8bdf030613bb9c8d",     "X-B3-Traceid": "75d84f317abad5232a739cfc42d28236",     "X-Envoy-External-Address": "10.100.91.201",     "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/httpbin-istio/sa/httpbin;Hash=ea8c3d70befa0d73aa0f07fdb74ec4700d42a72889a04630741193548f1a7ae1;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"  },   "json": null,   "origin": "69.84.111.39,10.100.91.201",   "url": "http://httpbin.org/post"}

Обратите внимание, что по-умолчанию значением заголовка Content-Type является application/x-www-form-urlencoded. Для json это является неверным, поэтому для определения Content-Type следует использовать флаг -H:

$ curl -X POST -d '{"answer":42}' -H "Content-Type: application/json" https://httpbin.org/post{  "args": {},   "data": "{\"answer\":42}",   "files": {},   "form": {},   "headers": {    "Accept": "*/*",     "Content-Length": "13",     "Content-Type": "application/json",     "Host": "httpbin.org",     "User-Agent": "curl/7.58.0",     "X-Amzn-Trace-Id": "Root=1-5ee8e45e-ad875af4f83efd4379b86c34",     "X-B3-Parentspanid": "5f4f33d1c5ea13aa",     "X-B3-Sampled": "0",     "X-B3-Spanid": "a062c9bf2ebfd4bd",     "X-B3-Traceid": "44aa8d62412ae34d5f4f33d1c5ea13aa",     "X-Envoy-External-Address": "10.100.86.47",     "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/httpbin-istio/sa/httpbin;Hash=2f0b3331fe4d512975b4b82583a55dd5d1196023d0dfce9e0abed246991c5b67;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"  },   "json": {    "answer": 42  },   "origin": "69.84.111.39,10.100.86.47",   "url": "http://httpbin.org/post"}

Отправка PUT-запроса, содержащего JSON-файл


Флаг -d также поддерживает отправку данных из локальных файлов.

Например, представим, что у нас есть файл data.js, содержащий такие данные:

{"answer": 42}

Для отправки PUT-запроса с этим файлом в качестве тела запроса вы можете присвоить флагу -d значение @data.json. Префикс @ говорит curl загрузить тело запроса из файла data.json:

$ curl -X PUT -d '@data.json' -H "Content-Type: application/json" https://httpbin.org/put{  "args": {},   "data": "{\"answer\":42}",   "files": {},   "form": {},   "headers": {    "Accept": "*/*",     "Content-Length": "13",     "Content-Type": "application/json",     "Host": "httpbin.org",     "User-Agent": "curl/7.58.0",     "X-Amzn-Trace-Id": "Root=1-5ee8e745-37c4ef06326b7b4354a16b94",     "X-B3-Parentspanid": "a4f8f91f4f1b051e",     "X-B3-Sampled": "0",     "X-B3-Spanid": "a018b1a3fcebdc68",     "X-B3-Traceid": "7b48b01dc3f632eea4f8f91f4f1b051e",     "X-Envoy-External-Address": "10.100.91.201",     "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/httpbin-istio/sa/httpbin;Hash=6035260d9d551af6c1907270653214e8d3195abbdd19078c1c84fd9a4106f260;Subject=\"\";URI=spiffe://cluster.local/ns/istio-system/sa/istio-ingressgateway-service-account"  },   "json": {    "answer": 42  },   "origin": "69.84.111.39,10.100.91.201",   "url": "http://httpbin.org/put"}

Заключение


Резюмируя, вот опции curl, которые я нахожу самыми полезными:

  • -X определяет метод запроса, например, curl -X POST url
  • -d определяет тело запроса в виде строки в PUT и POST-запросах. Используйте @ для извлечения данных из файла
  • -H определяет заголовок запроса, например, curl -H "Authorization: my-secret-token" url
  • -u аутентификационные данные для стандартной авторизации
  • -O сохраняет тело запроса в файл
  • -i показывает полный ответ, включая заголовки

Curl полезный инструмент взаимодействия с API посредством командной строки, независимо от того, сторонний это API или API, который вы разрабатываете. Для быстрого тестирования curl подходит лучше, чем Axios в Node.js или настройка запроса в Postman, если вы знакомы с их синтаксисом.

Благодарю за потраченное время. Надеюсь, оно было потрачено не зря.
Подробнее..

Перевод Если вы пишете код в Windows, вы заслуживаете лучшего терминала

17.04.2021 18:19:17 | Автор: admin

Я хочу сделать признание. Когда дело доходит до моего компьютера, я оставляю все в значительной степени сыром виде. Конечно, у меня есть любимые маленькие инструменты. Я использую плагины Chrome, такие как Wappalyzer, и множество расширений VS Code, таких как Chrome Debugger и Live Server. Но я сознательно не использую темы, шрифты, средства форматирования и другие приятные для глаз настройки. В далеком прошлом, когда я только начинал программировать, я тратил слишком много времени на перестройку своей индивидуальной настройки на разных компьютерах и на новом оборудовании. Постоянные настройки устарели, поэтому я решил по возможности сократить до стокового.

Это мое оправдание, почему я провел много месяцев, по большей части игнорируя продукт Microsoft Windows Terminal. В конце концов, время, которое я провожу в командной строке, ограничено и ничем не примечательно. Я настраиваю свое приложение, устанавливаю пакеты npm или Nuget и двигаюсь дальше. Проводить время в окне терминала означает заходить в темный угол операционной системы и делать то, что нужно.

Но теперь я вынужден признать, что был неправ. Или, по крайней мере, есть еще один инструмент, для которого мне нужно освободить место. Поскольку Windows Terminal не просто заменяет скрипучую часть программного обеспечения ОС с кодовой базой 30-летней давности, он также добавляет некоторые действительно практичные функции.

Кодовой базе Windows Console 30 лет на самом деле она старше, чем разработчики, которые сейчас над ней работают. - Рич Тернер, менеджер по Microsoft

Терминал открыт

Прежде чем продолжить, стоит сделать краткий обзор того, что такое терминал на самом деле. Это потому, что мы, разработчики, работающие в Windows, привыкли объединять терминалы и программы оболочки в одну расплывчатую идею. Например, вы можете подумать, что когда Windows перешла со старой командной строки на объектно-ориентированную оболочку PowerShell, она заменила программу терминала. Но на самом деле это неправда.

Терминал - это часть программного обеспечения, которое обрабатывает текстовый ввод и отображение. Вы вводите текст в командной строке терминала. Вы смотрите в окно терминала. Но за кулисами ваш терминал взаимодействует с программой оболочки, которая действительно выполняет эту работу. В современной Windows стандартная программа терминала - ConHost.exe, и, черт возьми, она старая.

Вы думаете, что запускаете PowerShell, но на самом деле вы запускаете интерфейс ConHost, который взаимодействует с PowerShell.

Microsoft очень не хочет что-либо менять в работе ConHost, потому что это стержень вековой обратной совместимости. Фактически, основной принцип дизайна ConHost - не нарушать обратной совместимости любой ценой. Даже исправление ошибок рискует уничтожить век сценариев и инструментов, которые каким-то образом все еще работают в режиме совместимости в современной Windows.

Вместо этого Microsoft начала создавать новый терминал под названием Windows Terminal. Он существует уже почти год, но еще не дошел до включения в ОС Windows. Это означает, что если вам нужен Терминал Windows, вы должны установить его из Windows Store. (Или вы можете загрузить его с GitHub и собрать самостоятельно, потому что новый терминал, естественно, имеет открытый исходный код.)

Почему терминал Windows?

Из-за того, как работают терминалы, в них не так много очевидного волшебства. Фактически, выполнение работы выполняется любой программой оболочки, которую вы используете. Но оказывается, что новый терминал Windows содержит множество практических удобств, которые могут сделать вас более продуктивным (или, по крайней мере, менее раздражающим) при выполнении повседневной работы. Вот несколько причин полюбить Windows Terminal:

  • Несколько вкладок. Помните, когда в веб-браузерах была только одна вкладка? Как мы это ненавидели! Но мы терпели это в ConHost уже целое поколение. К счастью, Windows Terminal позволяет открывать столько вкладок, сколько нужно в одном окне.

    Иногда мелочи - это большие делаИногда мелочи - это большие дела
  • Несколько панелей. Это похоже на несколько вкладок, но вы можете видеть разные экземпляры терминала в аккуратном порядке бок о бок или сверху и снизу. И вы управляете всем этим с помощью удобных нажатий клавиш. Удерживая Alt + Shift, нажмите +, чтобы открыть новую панель справа, или -, чтобы открыть новую панель внизу. Затем вы можете переходить с панели на панель, удерживая Alt и нажимая клавиши со стрелками. Круто!

  • Одновременное использование нескольких оболочек. Терминал Windows поддерживает любую стандартную программу оболочки. Вы можете использовать старую добрую PowerShell, почти устаревшую командную строку, Azure Cloud Shell (для управления онлайн-ресурсами Azure) и даже bash, если вы включили Windows Linux Subsystem. И вы можете запускать их все рядом, на разных вкладках или панелях одного и того же окна Терминала Windows.

    Оболочки сошли с умаОболочки сошли с ума
  • Масштабирование, которое работает. Мое любимое сочетание клавиш масштабирования - удерживать Ctrl и вращать колесико мыши. Это работает и в ConHost, но при этом неудобно изменяет размер окна. Терминал Windows масштабирует более разумно, и он распознает удобное сочетание клавиш Ctrl + 0, чтобы вернуть все в нормальное состояние. И не повредит, что Windows Terminal поставляется с новым элегантным шрифтом Cascadia Code, который отлично смотрится при любом размере.

  • Современный курсор. Что это за блочная штука в ConHost? Он показывает вашу текущую позицию, а не точку вставки, поэтому легко забыть, если нажатие клавиши вставляет до или после текущего символа.

  • Изобилие настроек. Все они управляются через немного непонятный файл настроек JSON. Освойте его, чтобы управлять внешним видом окна терминала (размером, цветами, настройкой всегда поверх) и добавьте свои собственные сочетания клавиш.

Пользователи Linux скажут вам, что многие из этих функций у них были в течение многих лет. Опытные разработчики Windows скажут вам то же самое, потому что они уже используют какую-то альтернативу терминалу с открытым исходным кодом. В основном они правы, но теперь вам не нужно игнорировать их в тихом смущении.

Терминал Windows также имеет графическое оформление, которое мне кажется изящным и почти бесполезным. Мне было интересно поиграть с этими функциями около 90 секунд, а потом забыть на всю оставшуюся жизнь:

  • Настраиваемая прозрачность с размытием фона. Вы даже можете настроить его на лету, удерживая Ctrl + Shift и вращая кнопку мыши. Но зачем?

  • Цветовые схемы и пользовательские фоновые изображения.

  • Анимированные фоны в формате GIF. (Привет, Windows Plus примерно из 1998 года.)

Конечно, если вы решите использовать эти функции, я не буду судить.

Краткое примечание о терминале VS Code

Если вы используете Visual Studio Code, вы, вероятно, знакомы с его интегрированным терминалом. Вы можете выбрать, какую оболочку использовать (например, PowerShell или bash), но вы всегда используете терминал VS Code, а не ConHost.

Тем не менее, терминал VS Code довольно прост. Терминал Windows не может заменить встроенный терминал. Однако вы можете настроить Windows Terminal так, чтобы он работал как внешний терминал для VS Code. Таким образом, когда вы запускаете терминал из VS Code, вы откроете отдельное окно Windows Terminal, что даст вам больше места для передышки и современные функции, которые вам действительно нужны.

Последнее слово

Терминал Windows неуклонно продвигается к версии 2.0, которая ожидается этой весной, и в конечном итоге включение в Windows. Планируется длинный список новых функций, включая возможность отрывать вкладки и перемещать их из одного окна терминала. к другому, бесконечная прокрутка и приятный пользовательский интерфейс для управления настройками. Будет ли он вызывать безумную любовь, как VS Code или язык C #? Нет. Но иногда достаточно сделать жизнь менее болезненной.

Скачать Windows Terminal можно здесь.

Подробнее..

Разукрашиваем вывод в консоли теория и практика

23.05.2021 14:09:40 | Автор: admin


Консоль привлекает многих своей минималистичностью и эстетикой, но даже в ней иногда хочется выделить определённый фрагмент, чтобы показать его роль или значимость. Например, отметить зелёным текстом сообщение об успешном выполнении операции или обозначить длинный текст ошибки курсивом. О том, как это делать, а также о реализации на питоне читайте далее.


Управляющие последовательности ANSI


ANSI escape sequences или Управляющие последовательности ANSI это стандарт, дающий возможность управлять курсором, цветами, начертание в текстовых консолях. Такие последовательности воспринимаются отрисовщиком терминала, как команды отображать последующий текст в определенном формате. Есть также последовательность, которая сбрасывает предыдущие команды, и отображение текста становиться обычным. Существует несколько форматов управляющих последовательностей, различающихся возможностями и появившимися в разных версиях кодировок. Поговорим об этих форматах подробнее.


8 основных цветов и стили


Для обычного пользователь разнообразия цветов этого формата будет более чем исчерпывающим. Но прежде чем задумываться о том, что терракотовый цвет гораздо круче красного, и он вам уж точно нужен, давайте разберемся, как устроена самая простая версия управляющей последовательности ANSI для форматирования текста.


Чтобы изменить текущий цвет шрифта или фона можно использовать следущий синтаксис:


  • Начинается управляющая последовательность с любого из этих трёх представлений: \x1b[ (hex) или \u001b[ (Unicode) или \033[ (oct)
  • Далее следуют аргументы, разделённые между собой ;(можно указывать в любом порядке)
  • В конце ставится буква m

Возможные аргументы


  • Изменения стиля


    Модификатор Код
    1 Жирный
    2 Блеклый
    3 Курсив
    4 Подчёркнутый
    5 Мигание
    9 Зачёркнутый

  • Изменения цвета шрифта


    Цвет Код
    30 Чёрный
    31 Красный
    32 Зелёный
    33 Жёлтый
    34 Синий
    35 Фиолетовый
    36 Бирюзовый
    37 Белый

  • Изменения цвета фона


    Цвет Код
    40 Чёрный
    41 Красный
    42 Зелёный
    43 Жёлтый
    44 Синий
    45 Фиолетовый
    46 Бирюзовый
    47 Белый


Бонус: другие интересные модификаторы, которые могут поддерживаться не всеми платформами


Модификатор Код
38 RGB цвет (см. раздел "Совсем много цветов")
21 Двойное подчёркивание
51 Обрамлённый
52 Окружённый
53 Надчёркнутый

Пример корректного синтаксиса: \033[3;36;44m. После вывода этой конструкции стиль будет изменён для всего последующего текста. Чтобы вернуться к изначальному состоянию можно использовать \033[0m, тогда весь текст с этого места вернётся к изначальному форматированию.


Давайте поэкспементируем. Для примеров я буду использовать Python.



Важно заметить, что форматирование повлияло и на консоль питона, а не только на ее вывод. Именно поэтому очень важно закрывать все "тэги" изменения форматирования.


Часто используемые сочетания (copy-paste-able)


Код Описание
\033[0m вернуться к начальному стилю
\033[31m <your text goes here> \033[0m красный текст для обозначения ошибок
\033[1;31m <your text goes here> \033[0m жирный красный текст для обозначения критических ошибок
\033[32m <your text goes here> \033[0m зеленый текст успешное выполнение
\033[3;31m <your text goes here> \033[0m красный курсив текст ошибки
\033[43m <your text goes here> \033[0m выделение основного, как будто жёлтым маркером

Больше цветов: аж целых 256


Некоторые терминалы поддерживают вывод целых 256 цветов. Если команда echo $TERM выводит xterm-256color, то ваш терминал всё корректно обработает.


В этом формате синтаксис немного другой:



Для генерации кодов цветов можно использовать генератор.


А палитру доступных цветов можно увидеть на картинке ниже.


Палитра цветов


Совсем много цветов


Этот формат не всегда поддерживается стандартными консолями.


Некотрые будут негодовать: "256 цветов и нет моего любимого терракотового, какой ужас!". Для таких ценителей существует формат, который уже поддерживает 24 битные цвета (3 канала RGB по 256 градаций).
Для не ценителей поясню, что терракотовый кодируется как (201, 100, 59) или #c9643b.
Синтаксис в этом формате выглядит вот так:


  • \033[38;2;r;g;bm цвет текста
  • \033[48;2;r;g;bm цвет фона


Python: Использование библиотеки Colorama


Библиотека Colorama позволяет форматировать текст, не запоминая коды цветов. Рассмотрим её использование на примере:


from colorama import init, Fore, Back, Styleinit()print(Fore.RED + 'some red text\n' + Back.YELLOW + 'and with a yellow background')print(Style.DIM + 'and in dim text\n' + Style.RESET_ALL + 'back to normal now')

Вывод программы:



Style позволяет изменить стиль, Fore цвет шрифта, Back цвет фона. Использовать переменные из colorama нужно также, как и коды изменения стиля. Но плюс использования библиотеки в том, что Fore.RED более читаем, чем \033[0;31m


Если в colorama.init() указать параметр autoreset=True, то стиль будет автоматически сбрасываться (в конец каждого print будут добавлены сбрасывающие стили последовательности), поэтому вам не придётся об этом каждый раз вспоминать.


А что не так с Windows?


Просто так синтаксис, описанный в начале статьи, не работает в командной строке Windows. Поддержка цветов появлялась постепенно, причём в странном варианте. В начале программы надо сделать системный вызов, активирующий отрисовку цветов. А в более старых версиях необходимо заменить все ANSI последовательности на системные вызовы.


Но colorama.init() сделает всё за вас в большинстве версий Windows. Однако если вы используете другую операционную систему, то функцию init() вызывать в начале программы не обязательно. Также некоторые IDE на Windows (например, PyCharm) тоже поддерживают цвета без каких-либо махинаций.
А еще Windows не поддерживает многие модификаторы, такие как жирный текст. Подробнее можно почитать на странице Colorama


Termcolor


Ещё одна библиотека для вывода цветного текста с более удачным, на мой взлгяд, синтаксисом.


from termcolor import colored, cprinttext = colored('Hello, Habr!', 'red', attrs=['blink'])print(text)cprint('Hello, Habr!', 'green', 'on_red')


Кстати, проблему с Windows всё ещё можно починить с помощью colorama.init()


Выводы


Стандартные 8 цветов позволяют разнообразить вывод в консоль и расставить акценты. 256 цветов намного расширяют возможности, хотя и поддерживаются не всеми консолями. Windows, к сожалению, не поддерживает многие основные модификаторы, например, курсив. Также есть некоторые цвета, которые не прописаны в стандартах, но могут поддерживаться вашей операционной системой. Если вы хотите больше цветов, то вы можете поискать их в Гугле.
Пока что не любой терминал поддерживает 24-битные цвета и все модификаторы, но мы вряд ли увидим сильные изменения в этой сфере. Так что пока нам остаётся выбирать самые красивые варианты из тех, что доступны в любимом терминале.


Источники





Облачные серверы от Маклауд быстрые и безопасные.


Зарегистрируйтесь по ссылке выше или кликнув на баннер и получите 10% скидку на первый месяц аренды сервера любой конфигурации!


Подробнее..

CRI-O как замена Docker в качестве исполняемой среды для Kubernetes настройка на CentOS 8

20.08.2020 12:11:50 | Автор: admin
Привет! Меня зовут Сергей, я DevOps в Surf. DevOps-отдел в Surf ставит своей задачей не только налаживание взаимодействия между специалистами и интеграцию рабочих процессов, но и активные исследования и внедрение актуальных технологий как в собственную инфраструктуру, так и в инфраструктуру заказчика.

Ниже я немного расскажу об изменениях в технологическом стеке для контейнеров, с которыми мы встретились при изучении дистрибутива CentOS 8 и о том, что такое CRI-O и как быстро настроить с его помощью исполняемую среду для Kubernetes.



Почему Docker отсутствует в стандартной поставке CentOS 8


После установки последних крупных релизов RHEL 8 или CentOS 8 нельзя не заметить: в этих дистрибутивах и официальных репозиториях отсутствует приложение Docker, которое идеологически и функционально заменяют собой пакеты Podman, Buildah (присутствуют в дистрибутиве по умолчанию) и CRI-O. Это связано с практической реализацией стандартов, разрабатываемых, в том числе, и компанией Red Hat в рамках проекта Open Container Initiative (OCI).

Цель OCI, являющейся частью The Linux Foundation, создание открытых индустриальных стандартов для форматов и исполняемой среды контейнеров, которые бы решали сразу несколько задач. Во-первых, не противоречили как философии Linux (например, в той её части, что каждая программа должна выполнять какое-то одно действие, а Docker представляет собой этакий комбайн всё-в-одном). Во-вторых, могли бы устранить все имеющиеся недостатки в программном обеспечении Docker. В-третьих, были бы полностью совместимыми с бизнес-требованиями, выдвигаемыми ведущими коммерческими платформами для развёртывания, управления и обслуживания контейнеризованных приложений (например, Red Hat OpenShift).

Недостатки Docker и достоинства нового ПО уже были довольно подробно описаны в этой статье, а с подробным описанием как всего предлагаемого в рамках проекта OCI стека ПО и его архитектурными особенностями можно ознакомиться в официальной документации и статьях как от самой Red Hat (неплохая статья в Red Hat blog), так и в сторонних обзорах.

Важно отметить, какую функциональность имеют компоненты предлагаемого стека:

  • Podman непосредственное взаимодействие с контейнерами и хранилищем образов через процесс runC;
  • Buildah сборка и загрузка в реестр образов;
  • CRI-O исполняемая среда для систем оркестрации контейнеров (например, Kubernetes).

Думаю, что для понимания общей схемы взаимодействия между компонентами стека целесообразно привести здесь схему связей Kubernetes c runC и низкоуровневыми библиотеками с использованием CRI-O:



CRI-O и Kubernetes придерживаются одного и того же цикла выпуска и поддержки (матрица совместимости очень проста: мажорные версии Kubernetes и CRI-O совпадают), а это, с учётом ориентира на полное и всестороннее тестирование работы данного стека разработчиками, даёт нам право ожидать максимально достижимой стабильности в работе при любых сценариях использования (здесь на пользу идет и относительная легковесность CRI-O по сравнению с Docker в силу целенаправленного ограничения функциональности).

При установке Kubernetes right way способом (по мнению OCI, конечно) с использованием CRI-O на CentOS 8 мы столкнулись с небольшими затруднениями, которые, однако, успешно преодолели. Буду рад поделиться с вами инструкцией по установке и настройке, которые в совокупности займут от силы 10 минут.

Как развернуть Kubernetes на CentOS 8 с использованием среды CRI-O


Предварительные условия: наличие как минимум одного хоста (2 cores, 4 GB RAM, накопитель не менее 15 GB) с установленной CentOS 8 (рекомендуется профиль установки Server), а также записи для него в локальном DNS (в крайнем случае можно обойтись записью в /etc/hosts). И не забудьте отключить swap.

Все операции на хосте производим от имени пользователя root, будьте внимательны.

  1. На первом шаге настроим ОС, установим и настроим предварительные зависимости для CRI-O.
    • Обновим ОС:

      dnf -y update
      

    • Далее требуется настроить файрволл и SELinux. Здесь у нас всё зависит от окружения, в котором будут работать наш хост или хосты. Вы можете либо настроить файрволл по рекомендациям из документации, либо, если находитесь в доверенной сети или применяете сторонний файрволл, изменить зону по умолчанию на доверенную или выключить файрволл:

      firewall-cmd --set-default-zone trustedfirewall-cmd --reload
      

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

      systemctl disable --now firewalld
      

      SELinux требуется выключить либо перевести в режим permissive:

      setenforce 0sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
      

    • загрузим необходимые модули ядра и пакеты, настроим автоматическую загрузку модуля br_netfilter при старте системы:

      modprobe overlaymodprobe br_netfilterecho "br_netfilter" >> /etc/modules-load.d/br_netfilter.confdnf -y install iproute-tc
      

    • для активации форвардинга пакетов и корректной обработки трафика сделаем соответствующие настройки:

      cat > /etc/sysctl.d/99-kubernetes-cri.conf <<EOFnet.bridge.bridge-nf-call-iptables = 1net.ipv4.ip_forward = 1net.bridge.bridge-nf-call-ip6tables = 1EOF
      

      применим сделанные настройки:

      sysctl --system
      

    • зададим необходимую версию CRI-O (мажорная версия CRI-O, как уже упоминалось, совпадают с требуемой версией Kubernetes), так как последняя стабильная версия Kubernetes на данный момент 1.18:

      export REQUIRED_VERSION=1.18
      

      добавим необходимые репозитории:

      dnf -y install 'dnf-command(copr)'dnf -y copr enable rhcontainerbot/container-selinuxcurl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/CentOS_8/devel:kubic:libcontainers:stable.repocurl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable:cri-o:$REQUIRED_VERSION.repo https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable:cri-o:$REQUIRED_VERSION/CentOS_8/devel:kubic:libcontainers:stable:cri-o:$REQUIRED_VERSION.repo
      

    • теперь мы можем установить CRI-O:

      dnf -y install cri-o
      

      Обратите внимание на первый нюанс, который мы встречаем в процессе инсталляции: необходимо отредактировать конфигурацию CRI-O перед запуском сервиса, так как требуемый компонент conmon имеет отличное от указанного место размещения:

      sed -i 's/\/usr\/libexec\/crio\/conmon/\/usr\/bin\/conmon/' /etc/crio/crio.conf
      


      Теперь можно активировать и запустить демон CRI-O:

      systemctl enable --now crio
      


      Можно проверить статус демона:

      systemctl status crio
      

  2. Установка и активация Kubernetes.
    • Добавим требуемый репозиторий:

      cat <<EOF > /etc/yum.repos.d/kubernetes.repo[kubernetes]name=Kubernetesbaseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearchenabled=1gpgcheck=1repo_gpgcheck=1gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpgexclude=kubelet kubeadm kubectlEOF
      

      Теперь мы можем установить Kubernetes (версии 1.18, как уже указывалось выше):

      dnf install -y kubelet-1.18* kubeadm-1.18* kubectl-1.18* --disableexcludes=kubernetes
      

    • Второй важный нюанс: так как мы не используем демон Docker, а используем демон CRI-O, до запуска и инициализации Kubernetes требуется внести соответствующие настройки в конфигурационный файл /var/lib/kubelet/config.yaml, предварительно создав нужный каталог:

      mkdir /var/lib/kubeletcat <<EOF > /var/lib/kubelet/config.yamlapiVersion: kubelet.config.k8s.io/v1beta1kind: KubeletConfigurationcgroupDriver: systemdEOF
      

    • Третий важный момент, с которым мы сталкиваемся при установке: несмотря на то, что мы указали используемый драйвер cgroup, и его настройка через аргументы передаваемые kubelet устарела (на что прямо указано в документации), нам необходимо добавить в файл аргументы, иначе наш кластер не инициализируется:

      cat /dev/null > /etc/sysconfig/kubeletcat <<EOF > /etc/sysconfig/kubeletKUBELET_EXTRA_ARGS=--container-runtime=remote --cgroup-driver=systemd --container-runtime-endpoint='unix:///var/run/crio/crio.sock'EOF
      

    • Теперь мы можем активировать демон kubelet:

      sudo systemctl enable --now kubelet
      

      Чтобы настроить control-plane или worker ноды за считанные минуты, вы можете воспользоваться этим скриптом.

  3. Пора инициализировать наш кластер.
    • Для инициализации кластера выполните команду:

      kubeadm init --pod-network-cidr=10.244.0.0/16
      

      Обязательно запишите команду присоединения к кластеру kubeadm join ..., которой предлагается воспользоваться в конце вывода, либо, как минимум, указанные токены.

    • Установим плагин (CNI) для работы Pod network. Я рекомендую использовать Calico. Возможно, более популярный Flannel имеет проблемы с совместимостью с nftables, да и Calico единственная реализация CNI, рекомендуемая и полностью протестированная проектом Kubernetes:

      kubectl --kubeconfig /etc/kubernetes/admin.conf apply -f https://docs.projectcalico.org/v3.15/manifests/calico.yaml 
      

    • Для подключения worker ноды к нашему кластеру её требуется настроить по пунктам инструкции 1 и 2, либо воспользоваться скриптом, затем выполнить команду из вывода kubeadm init ..., которую мы записали на предыдущем этапе:

      kubeadm join $CONTROL_PLANE_ADDRESS:6443 --token $TOKEN \    --discovery-token-ca-cert-hash $TOKEN_HASH
      

    • Проверим, что наш кластер инициализирован и начал работу:

      kubectl --kubeconfig=/etc/kubernetes/admin.conf get pods -A
      

    Готово! Вы уже можете размещать на вашем K8s кластере полезную нагрузку.

Что нас ждёт впереди


Надеюсь, что инструкция выше помогла сэкономить вам немного времени и нервов.
Исход процессов, происходящих в индустрии, зачастую зависит от того, как их принимает основная масса конечных пользователей и разработчиков другого ПО в соответствующей нише. Пока не совсем ясно, к какому итогу через несколько лет приведут инициативы OCI, но мы будем с удовольствием за этим следить. Своим мнением вы можете поделиться прямо сейчас в комментариях.
Stay tuned!

Данная статья появилась благодаря следующим источникам:

Подробнее..

Начало работы с Windows Terminal

22.12.2020 10:22:28 | Автор: admin
Привет, Хабр! Сегодня делимся гайдом по началу работы с Windows Terminal. Да, поскольку он о начале работы с инструментом, в основном в материале описываются какие-то базовые моменты. Но я думаю, что и профессионалы смогут подчерпнуть для себя что-то полезное, как минимум из списка полезных ссылок в конце статьи. Заглядывайте под кат!



Установка


Windows Terminal доступен в двух разных сборках: Windows Terminal и Windows Terminal Preview. Обе сборки доступны для загрузки в Microsoft Store и на странице выпусков GitHub.

Требования


Для запуска любой сборки Windows Terminal на вашем компьютере должна быть установлена Windows 10 1903 или более поздняя версия.

Windows Terminal Preview


Windows Terminal Preview это сборка, в которой в первую очередь появляются новые функции. Эта сборка предназначена для тех, кто хочет видеть новейшие функции сразу после их выпуска. Эта сборка имеет ежемесячный цикл выпуска с новейшими функциями каждый месяц.

Windows Terminal


Терминал Windows это основная сборка продукта. Функции, которые поступают в Windows Terminal Preview, появляются в Windows Terminal через месяц эксплуатации. Это позволяет проводить обширное тестирование ошибок и стабилизацию новых функций. Эта сборка предназначена для тех, кто хочет получить функции после того, как они были изучены и протестированы сообществом Preview.

Первый запуск


После установки терминала вы можете запустить приложение и сразу приступить к работе с командной строкой. По умолчанию терминал включает профили Windows PowerShell, Command Prompt и Azure Cloud Shell в раскрывающемся списке. Если на вашем компьютере установлены дистрибутивы Подсистемы Windows для Linux (WSL), они также должны динамически заполняться как профили при первом запуске терминала.

Профили


Профили действуют как различные среды командной строки, которые вы можете настраивать внутри терминала. По умолчанию в каждом профиле используется отдельный исполняемый файл командной строки, однако вы можете создать столько профилей, сколько захотите, используя один и тот же исполняемый файл. Каждый профиль может иметь свои собственные настройки, которые помогут вам различать их и добавить в каждый свой собственный стиль.



Дефолтный профиль


При первом запуске Windows Terminal в качестве профиля по умолчанию устанавливается Windows PowerShell. Профиль по умолчанию это профиль, который всегда открывается при запуске терминала, и это профиль, который открывается при нажатии кнопки новой вкладки. Вы можете изменить профиль по умолчанию, установив defaultProfile на имя вашего предпочтительного профиля в файле settings.json.

"defaultProfile": "PowerShell"

Добавление нового профиля


Новые профили можно добавлять динамически с помощью терминала или вручную. Терминал Windows автоматически создаст профили для распределений PowerShell и WSL. Эти профили будут иметь свойство source, которое сообщает терминалу, где он может найти соответствующий исполняемый файл.

Если вы хотите создать новый профиль вручную, вам просто нужно сгенерировать новый guid, указать name и предоставить исполняемый файл для свойства commandline.

Примечание. Вы не сможете скопировать свойство source из динамически созданного профиля. Терминал просто проигнорирует этот профиль. Вам нужно будет заменить source на commandline и предоставить исполняемый файл, чтобы дублировать динамически созданный профиль.

Структура Settings.json


В Терминал Windows включены два файла настроек. Один из них defaults.json, который можно открыть, удерживая клавишу Alt и нажав кнопку Настройки в раскрывающемся списке. Это неизменяемый файл, который включает в себя все настройки по умолчанию, которые поставляются с терминалом. Второй файл settings.json, в котором вы можете применить все свои пользовательские настройки. Доступ к нему можно получить, нажав кнопку Настройки в раскрывающемся меню.

Файл settings.json разделен на четыре основных раздела. Первый это объект глобальных настроек, который находится в верхней части файла JSON внутри первого {. Примененные здесь настройки повлияют на все приложение.

Следующим основным разделом файла является объект profiles. Объект profiles разделен на два раздела: defaults и list. Вы можете применить настройки профиля к объекту defaults, и они будут применяться ко всем профилям в вашем list. list содержит каждый объект профиля, который представляет профили, описанные выше, и это элементы, которые появляются в раскрывающемся меню вашего терминала. Настройки, примененные к отдельным профилям в списке, имеют приоритет над настройками, примененными в разделе defaults.

Далее в файле расположен массив schemes. Здесь можно разместить собственные цветовые схемы. Отличный инструмент, который поможет вам создать свои собственные цветовые схемы, это terminal.sexy.

Наконец, в нижней части файла находится массив actions. Перечисленные здесь объекты добавляют действия в ваш терминал, которые можно вызывать с клавиатуры и/или находить внутри палитры команд.

Базовая кастомизация


Вот несколько основных настроек, которые помогут вам начать настройку вашего терминала.

Фон


Одна из самых популярных настроек настраиваемое фоновое изображение. Это настройка профиля, поэтому ее можно либо поместить внутри объекта defaults внутри объекта profiles, чтобы применить ко всем профилям, либо внутри определенного объекта профиля.

"backgroundImage": "C:\Users\admin\background.png"

Параметр backgroundImage принимает расположение файла изображения, которое вы хотите использовать в качестве фона вашего профиля. Допустимые типы файлов: .jpg, .png, .bmp, .tiff, .ico и .gif.



Цветовая схема


Список доступных цветовых схем можно найти на нашем сайте документации. Цветовые схемы применяются на уровне профиля, поэтому вы можете поместить настройку внутри значений по умолчанию или в конкретный объект профиля.

"colorScheme": "COLOR SCHEME NAME"

Этот параметр принимает название цветовой схемы. Вы также можете создать свою собственную цветовую схему и поместить ее в список schemes, а затем установить в настройках профиля имя этой новой схемы, чтобы применить ее.

Начертание шрифта


По умолчанию Windows Terminal использует Cascadia Mono в качестве шрифта. Начертание шрифта это настройка уровня профиля. Вы можете изменить шрифт, установив fontFace на имя шрифта, который вы хотите использовать.

"fontFace": "FONT NAME"`

Совет: Терминал Windows также поставляется с начертанием шрифта Cascadia Code, который включает программные лигатуры (см. Gif ниже). Если вы используете Powerline, Cascadia Code также поставляется в PL-версии, которую можно загрузить с GitHub.



Полезные ресурсы


Докуметация Windows Terminal
Скотт Хансельман: как сделать красивым Windows Terminal с помощью Powerline, шрифтов Nerd, кода Cascadia, WSL и oh-my-posh
Скотт Хансельман: Как настроить терминал с помощью Git Branch, Windows Terminal, PowerShell, + Cascadia Code!
Скотт Хансельман: Windows Terminal Feature PREVIEW Кастомизируйте свои привязки клавиш, цветовые схемы, панели, и многое другое!
>_TerminalSplash темы Windows Terminal
Подробнее..

Из песочницы Пишем автодополнение для ваших CLI проектов

20.08.2020 14:16:33 | Автор: admin

Приветствие


Всем привет! Хочу поделиться своим опытом написания кроссплатформенного проекта на C++ для интеграции автодополнения в CLI приложения, усаживайтесь поудобнее.




Формулировка задания


  • Приложение должно работать на Linux, macOS, Windows
  • Необходима возможность задавать правила для автодополнения
  • Предусмотреть наличие опечаток
  • Предусмотреть смену подсказок стрелками клавиатуры

Приготовления


Сразу предупрежу, использовать будем C++17


Предлагаю перейти к делу. Очевидно, так как наш проект кроссплатформенный, необходимо написать простенький макрос для определения текущей платформы.


#if defined(_WIN32) || defined(_WIN64)    #define OS_WINDOWS#elif defined(__APPLE__) || defined(__unix__) || defined(__unix)    #define OS_POSIX#else    #error unsupported platform#endif

Также сделаем небольшую заготовку:


#if defined(OS_WINDOWS)    #define ENTER 13    #define BACKSPACE 8    #define CTRL_C 3    #define LEFT 75    #define RIGHT 77    #define DEL 83    #define UP 72    #define DOWN 80    #define SPACE 32#elif defined(OS_POSIX)    #define ENTER 10    #define BACKSPACE 127    #define SPACE 32    #define LEFT 68    #define RIGHT 67    #define UP 65    #define DOWN 66    #define DEL 51#endif    #define TAB 9

Так как мы нацелены на CLI проекты, и терминалы Linux и macOS имеют одинаковый API, объединим их в один define OS_POSIX. Windows, как всегда, стоит в стороне, вынесем для нее отдельный define OS_WINDOWS.


Следующим шагом мы должны понять, как будут выглядеть автодополнения. С самого начала я был вдохновлен Redis CLI, поэтому будем просто выводить посказки другим нейтральным цветом. Но это не помешает нам в итоге использовать любые цвета.


Следовательно, требуется написать функцию установки нужного цвета для вывода в консоль:


/** * Sets the console color. * * @param color System code of target color. * @return Input parameter os. */#if defined(OS_WINDOWS)std::string set_console_color(uint16_t color) {    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);    return "";#elif defined(OS_POSIX)std::string set_console_color(std::string color) {    return "\033[" + color + "m";#endif}

Опять таки из-за разницы API приходится искать компромисс, будем всегда возвращать строку для того, чтобы можно было использовать функцию после оператора вывода << для повышения читаемости кода.


Для тех, кому интересно, как именно работает API для цвета в Posix и Windows, и какие цветовые профили вообще бывают, предлагаю почитать ответы добрых людей на stackoverflow:



Так как нам придется постоянно перерисовывать строку из-за подсказок, необходимо написать функцию для "стирания" строки.


/** * Get count of terminal cols. * * @return Width of terminal. */#if defined(OS_WINDOWS)size_t console_width() {    CONSOLE_SCREEN_BUFFER_INFO info;    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);    short width = --info.dwSize.X;    return size_t((width < 0) ? 0 : width);}#endif/** * Clear terminal line. * * @param os Output stream. * @return input parameter os. */std::ostream& clear_line(std::ostream& os) {#if defined(OS_WINDOWS)    size_t width = console_width();    os << '\r' << std::string(width, ' ');#elif defined(OS_POSIX)    std::cout << "\033[2K";#endif    return os;}

На Posix платформах все просто, достаточно вывести в консоль \033[2K, но естественно в Windows нет аналогов, конкретно я не смог найти, приходится писать свою реализацию.


Осталось понять, как осуществлять ввод символов пользователем. Обычный cin явно не подойдет, в процессе считывания не получится выводить предсказания.


Тут приходит на ум функция _getch(), доступная в Windows, которая получает код символа нажатой клавиши на клавиатуре это именно то, что нам надо. Но в этот раз с Posix платформами все плохо, увы, но придется писать свою реализацию.


#if defined(OS_POSIX)/** * Read key without press ENTER. * * @return Code of key on keyboard. */int _getch() {    int ch;    struct termios old_termios, new_termios;    tcgetattr( STDIN_FILENO, &old_termios );    new_termios = old_termios;    new_termios.c_lflag &= ~(ICANON | ECHO );    tcsetattr( STDIN_FILENO, TCSANOW, &new_termios );    ch = getchar();    tcsetattr( STDIN_FILENO, TCSANOW, &old_termios );    return ch;}#endif

Правила автодополнения




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


git    config        --global            user.name                "[name]"            user.email                "[email]"        user.name            "[name]"        user.email            "[email]"    init        [repository name]    clone        [url]

Идея такая. За каждым словом могут идти слова на расстоянии 1 табуляции от него. Т.е. после слова git могут идти слова config, init и global. После слова config могут идти слова --global, user.name и user.email и т.д. Также введем возможность указывать опциональные слова, в моем случае это слова внутри символов [] (вместо этих слов пользователь должен вводить свои данные).


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


typedef std::map<std::string, std::vector<std::string>> Dictionary;

Давайте напишем функцию для парсинга файла с правилами.


/** * Parse config file to dictionary. * * @param file_path The path to the configuration file. * @return Tuple of dictionary with autocomplete rules, status of parsing and message. */std::tuple<Dictionary, bool, std::string> parse_config_file(const std::string& file_path) {    Dictionary dict;            // Словарь с правилами автозаполнения    std::map<int, std::string>  // Массив для запоминания корневого слова    root_words_by_tabsize;      //  для определенной длины табуляции    std::string line;           // Строка для чтения    std::string token;          // Полученное слово из строки    std::string root_word;      // Корневое слово для вставки в словарь как ключ    long tab_size = 0;          // Базовая длина табуляции (пробелов)    long tab_count = 0;         // Колличество табуляций в строке    // Открытие файла конфигураций    std::ifstream config_file(file_path);    // Возвращаем сообщение об ошибке, если файл не был открыт    if (!config_file.is_open()) {        return std::make_tuple(            dict,            false,            "Error! Can't open " + file_path + " file."        );    }    // Считываем все строки    while (std::getline(config_file, line)) {        // Пропускаем строку если она пустая        if (line.empty()) {            continue;        }        // Если в файле обнаружен символ табуляции, возвращаем сообщение о ошибке        if (std::count(line.begin(), line.end(), '\t') != 0) {            return std::make_tuple(                dict,                false,                "Error! Use a sequence of spaces instead of a tab character."            );        }        // Получение количества пробелов в начале строки        auto spaces = std::count(            line.begin(),            line.begin() + line.find_first_not_of(" "),            ' '        );        // Устанавливаем базовый размер табуляции, если        // была найдена строка с пробелами в начале        if (spaces != 0 && tab_size == 0) {            tab_size = spaces;        }        // Получаем слово из строки        token = trim(line);        // Проверка длины табуляции        if (tab_size != 0 && spaces % tab_size != 0) {            return std::make_tuple(                dict,                false,                "Error! Tab length error was made.\nPossibly in line: " + line            );        }        // Получаем количество табуляций        tab_count = (tab_size == 0) ? 0 : (spaces / tab_size);        // Запоминаем корневое слово для заданного количества табуляций        root_words_by_tabsize[tab_count] = token;        // Получаем корневое слово для текущего токена        root_word = (tab_count == 0) ? "" : root_words_by_tabsize[tab_count - 1];        // Вставка токена в словарь, если его там нет        if (std::count(dict[root_word].begin(), dict[root_word].end(), token) == 0) {            dict[root_word].push_back(token);        }    }    // Закрываем файл    config_file.close();    // Если все ОК возвращаем готовый словарь    return std::make_tuple(        dict,        true,        "Success. The rule dictionary has been created."    );}

Разберемся с накопившимися вопросами.


  1. Функция возвращает кортеж, так как по моему использование исключений не очень удачный вариант.
  2. Почему использование символа \t в файле запрещено? Потому что будем привыкать к хорошей практике использования последовательности пробелов вместо табуляции.
  3. Откуда взялась функция trim, и что она делает? Сейчас покажу ее простую реализацию.

/** * Remove extra spaces to the left and right of the string. * * @param str Source string. * @return Line without spaces on the left and right. */std::string trim(std::string_view str) {    std::string result(str);    result.erase(0, result.find_first_not_of(" \n\r\t"));    result.erase(result.find_last_not_of(" \n\r\t") + 1);    return result;}

Функция просто отрезает лишнее пространство слева и справа у строки


Автодополнение


Хорошо. У нас есть словарь с правилами, а что дальше? Осталось сделать само автодополнение.
Представим, что пользователь вводит что-то с клавиатуры. Что мы имеем? Одно или несколько введенных слов.


Давайте научимся получать последнее слово из строки.


/** * Get the position of the beginning of the last word. * * @param str String with words. * @return Position of the beginning of the last word. */size_t get_last_word_pos(std::string_view str) {    // Вернуть 0 если строка состоит только из пробелов    if (std::count(str.begin(), str.end(), ' ') == str.length()) {        return 0;    }    // Получаем позицию последнего пробела    auto last_word_pos = str.rfind(' ');    // Вернуть 0, если пробел не найден, иначе вернуть позицию + 1    return (last_word_pos == std::string::npos) ? 0 : last_word_pos + 1;}/** * Get the last word in string. * * @param str String with words. * @return Pair Position of the beginning of the *         last word and the last word in string. */std::pair<size_t, std::string> get_last_word(std::string_view str) {    // Поулчаем позицию    size_t last_word_pos = get_last_word_pos(str);    // Получаем последнее слово из строки    auto last_word = str.substr(last_word_pos);    // Возвращаем пару из слова и позиции слова в строке (для удобства)    return std::make_pair(last_word_pos, last_word.data());}

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


Давайте научимся получать предпоследнее слово из строки.


// Не использовал std::min из-за странного // поведения MSVC компилятора/** * Get the minimum of two numbers. * * @param a First value. * @param b Second value. * @return Minimum of two numbers. */size_t min_of(size_t a, size_t b) {    return (a < b) ? a : b;}/** * Get the penultimate words. * * @param str String with words. * @return Pair Position of the beginning of the penultimate *         word and the penultimate word in string. */std::pair<size_t, std::string> get_penult_word(std::string_view str) {    // Находим правую границу поиска    size_t end_pos = min_of(str.find_last_not_of(' ') + 2, str.length());    // Получаем позицию начала последнего слова    size_t last_word = get_last_word_pos(str.substr(0, end_pos));    size_t penult_word_pos = 0;    std::string penult_word = "";    // Находим предпоследнее слово если позиция     // начала последнего была найдена    if (last_word != 0) {        // Находим начало предпоследнего слова        penult_word_pos = str.find_last_of(' ', last_word - 2);        // Находим предпоследнее слово если позиция начала найдена        if (penult_word_pos != std::string::npos) {            penult_word = str.substr(penult_word_pos, last_word - penult_word_pos - 1);        }        // Иначе предпоследнее слово - все, что дошло до последнего слова        else {            penult_word = str.substr(0, last_word - 1);        }    }    // Обрезаем строку    penult_word = trim(penult_word);    // Возвращаем пару из позиции и слова (для удобства)    return std::make_pair(penult_word_pos, penult_word);}

Нахождение слов для автодополнения




Что же мы забыли? Функцию для нахождения слов, которые начинаются также, как и последнее слово в строке.


/** * Find strings in vector starts with substring. * * @param substr String with which the word should begin. * @param penult_word Penultimate word in user-entered line. * @param dict Vector of words. * @param optional_brackets String with symbols for optional values. * @return Vector with words starts with substring. */std::vector<std::string>words_starts_with(std::string_view substr, std::string_view penult_word,                  Dictionary& dict, std::string_view optional_brackets) {    std::vector<std::string> result;    // Выход если нет ключа равного penult_word или    // substr имеет символы для опциональных слов     if (!dict.count(penult_word.data()) ||        substr.find_first_of(optional_brackets) != std::string::npos)     {        return result;    }    // Возвращаем все слова, которые могут быть     // после last_word, если substr пуста    if (substr.empty()) {        return dict[penult_word.data()];    }    // Находим строки, начинающиеся с substr    std::vector<std::string> candidates_list = dict[penult_word.data()];    for (size_t i = 0 ; i < candidates_list.size(); i++) {        if (candidates_list[i].find(substr) == 0) {            result.push_back(dict[penult_word.data()][i]);        }    }    return result;}

А что по поводу проверки орфографии? Мы же хотели ее добавить? Давайте сделаем это.


/** * Find strings in vector similar to a substring (max 1 error). * * @param substr String with which the word should begin. * @param penult_word Penultimate word in user-entered line. * @param dict Vector of words. * @param optional_brackets String with symbols for optional values. * @return Vector with words similar to a substring. */std::vector<std::string>words_similar_to(std::string_view substr, std::string_view penult_word,                  Dictionary& dict, std::string_view optional_brackets) {    std::vector<std::string> result;    // Выход, если строка пустая    if (substr.empty()) {        return result;    }    std::vector<std::string> candidates_list = dict[penult_word.data()];    for (size_t i = 0 ; i < candidates_list.size(); i++) {        int errors = 0;        // Получаем кандидата        std::string candidate = candidates_list[i];        // Посимвольная проверка кандидата        for (size_t j = 0; j < substr.length(); j++) {            // Пропуск, если кандидат содержит символы для опциональных слов            if (optional_brackets.find_first_of(candidate[j]) != std::string::npos) {                errors = 2;                break;            }            if (substr[j] != candidate[j]) {                errors += 1;            }            if (errors > 1) {                break;            }        }        // Добавляем кандидата, если максимум одна ошибка        if (errors <= 1) {            result.push_back(candidate);        }    }    return result;}

Теперь у нас есть все, чтобы предсказать слово по введенной пользователем строке.
Давайте решим эту задачу.


/** * Get the word-prediction by the index. * * @param buffer String with user input. * @param dict Dictionary with rules. * @param number Index of word-prediction. * @param optional_brackets String with symbols for optional values. * @return Tuple of word-prediction, phrase for output, substring of buffer *         preceding before phrase, start position of last word. */std::tuple<std::string, std::string, std::string, size_t>get_prediction(std::string_view buffer, Dictionary& dict, size_t number,               std::string_view optional_brackets) {    // Получаем информацию о последнем слове    auto [last_word_pos, last_word] = get_last_word(buffer);    // Получаем информацию о предпоследнем слове    auto [_, penult_word] = get_penult_word(buffer);    std::string prediction; // предсказание    std::string phrase;     // фраза для вывода    std::string prefix;     // подстрока буфера, предшествующая фразе    // Ищем предсказания    std::vector<std::string> starts_with = words_starts_with(        last_word, penult_word, dict, optional_brackets    );    // Устанавливаем значения, если предсказания были найдены    if (!starts_with.empty()) {        prediction = starts_with[number % starts_with.size()];        phrase = prediction;        prefix = buffer.substr(0, last_word_pos);    }    // Если слова не были найдены    else {        // Ищем слова с учетом орфографии        std::vector<std::string> similar = words_similar_to(            last_word, penult_word, dict, optional_brackets        );        // Устанавливаем значения, если предсказания были найдены        if (!similar.empty()) {            prediction = similar[number % similar.size()];            phrase = " maybe you mean " + prediction + "?";            prefix = buffer;        }    }    // Возвращаем необходимые данные    return std::make_tuple(prediction, phrase, prefix, last_word_pos);}

Ввод пользователя с клавиатуры




Осталось одно из самых сложных заданий. Написать саму функцию ввода с клавиатуры.


/** * Gets current terminal cursor position. * * @return Y position of terminal cursor. */short cursor_y_pos() {#if defined(OS_WINDOWS)    CONSOLE_SCREEN_BUFFER_INFO info;    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info);    return info.dwCursorPosition.Y;#elif defined(OS_POSIX)    struct termios term, restore;    char ch, buf[30] = {0};    short i = 0, pow = 1, y = 0;    tcgetattr(0, &term);    tcgetattr(0, &restore);    term.c_lflag &= ~(ICANON|ECHO);    tcsetattr(0, TCSANOW, &term);    write(1, "\033[6n", 4);    for (ch = 0; ch != 'R'; i++) {        read(0, &ch, 1);        buf[i] = ch;    }    i -= 2;    while (buf[i] != ';') {        i -= 1;    }    i -= 1;    while (buf[i] != '[') {        y = y + ( buf[i] - '0' ) * pow;        pow *= 10;        i -= 1;    }    tcsetattr(0, TCSANOW, &restore);    return y;#endif}/** * Move terminal cursor at position x and y. * * @param x X position to move. * @param x Y position to move. * @return Void. */void goto_xy(short x, short y) {#if defined(OS_WINDOWS)    COORD xy {--x, y};    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), xy);#elif defined(OS_POSIX)    printf("\033[%d;%dH", y, x);#endif}/** * Printing user input with prompts. * * @param buffer String - User input. * @param dict Vector of words. * @param line_title Line title of CLI when entering command. * @param number Hint number. * @param optional_brackets String with symbols for optional values. * @param title_color System code of title color     (line title color). * @param predict_color System code of predict color (prediction color). * @param default_color System code of default color (user input color). * @return Void. */#if defined(OS_WINDOWS)void print_with_prompts(std::string_view buffer, Dictionary& dict,                        std::string_view line_title, size_t number,                        std::string_view optional_brackets,                        uint16_t title_color, uint16_t predict_color,                        uint16_t default_color) {#elsevoid print_with_prompts(std::string_view buffer, Dictionary& dict,                        std::string_view line_title, size_t number,                        std::string_view optional_brackets,                        std::string title_color, std::string predict_color,                        std::string default_color) {#endif    // Получить прогнозируемую фразу и часть буфера, предшествующую фразе    auto [_, phrase, prefix, __] =         get_prediction(buffer, dict, number, optional_brackets);    std::string delimiter = line_title.empty() ? "" : " ";    std::cout << clear_line;    std::cout << '\r' << set_console_color(title_color) << line_title              << set_console_color(default_color) << delimiter << prefix              << set_console_color(predict_color) << phrase;    std::cout << '\r' << set_console_color(title_color) << line_title              << set_console_color(default_color) << delimiter << buffer;}/** * Reading user input with autocomplete. * * @param dict Vector of words. * @param optional_brackets String with symbols for optional values. * @param title_color System code of title color     (line title color). * @param predict_color System code of predict color (prediction color). * @param default_color System code of default color (user input color). * @return User input. */#if defined(OS_WINDOWS)std::string input(Dictionary& dict, std::string_view line_title,                  std::string_view optional_brackets, uint16_t title_color,                  uint16_t predict_color, uint16_t default_color) {#elsestd::string input(Dictionary& dict, std::string_view line_title,                  std::string_view optional_brackets, std::string title_color,                  std::string predict_color, std::string default_color) {#endif    std::string buffer;       // Буфер    size_t offset = 0;        // Смещение курсора от конца буфера    size_t number = 0;        // Номер (индекс) посдказки, для переключения    short y = cursor_y_pos(); // Получаем позицию курсора по оси Y в терминале    // Игнорируемые символы    #if defined(OS_WINDOWS)    std::vector<int> ignore_keys({1, 2, 19, 24, 26});    #elif defined(OS_POSIX)    std::vector<int> ignore_keys({1, 2, 4, 24});    #endif    while (true) {        // Выводим строку пользователя с предсказанием        print_with_prompts(buffer, dict, line_title, number, optional_brackets,                           title_color, predict_color, default_color);        // Перемещаем курсор в нужную позицию        short x = short(            buffer.length() + line_title.length() + !line_title.empty() + 1 - offset        );        goto_xy(x, y);        // Считываем очередной символ        int ch = _getch();        // Возвращаем буфер, если нажат Enter        if (ch == ENTER) {            return buffer;        }        // Обработка выхода из CLI в Windows        #if defined(OS_WINDOWS)        else if (ch == CTRL_C) {            exit(0);        }        #endif        // Изменение буфера при нажатии BACKSPACE        else if (ch == BACKSPACE) {            if (!buffer.empty() && buffer.length() - offset >= 1) {                buffer.erase(buffer.length() - offset - 1, 1);            }        }        // Применение подсказки при нажатии TAB        else if (ch == TAB) {            // Получаем необходимую информацию            auto [prediction, _, __, last_word_pos] =                 get_prediction(buffer, dict, number, optional_brackets);            // Дописываем предсказание, если имеется            if (!prediction.empty() &&                 prediction.find_first_of(optional_brackets) == std::string::npos) {                buffer = buffer.substr(0, last_word_pos) + prediction + " ";            }            // Очищаем индекс подсказки и смещение            offset = 0;            number = 0;        }        // Обработка стрелок        #if defined(OS_WINDOWS)        else if (ch == 0 || ch == 224)        #elif defined(OS_POSIX)        else if (ch == 27 && _getch() == 91)        #endif                switch (_getch()) {                    case LEFT:                        // Увеличьте смещение, если нажата левая клавиша                        offset = (offset < buffer.length())                                     ? offset + 1                                    : buffer.length();                        break;                    case RIGHT:                        // Уменьшить смещение, если нажата правая клавиша                        offset = (offset > 0) ? offset - 1 : 0;                        break;                    case UP:                        // Увеличить индекс подсказки                        number = number + 1;                        std::cout << clear_line;                        break;                    case DOWN:                        // Уменьшить индекс подсказки                        number = number - 1;                        std::cout << clear_line;                        break;                    case DEL:                    // Изменение буфера, при нажатии DELETE                    #if defined(OS_POSIX)                    if (_getch() == 126)                    #endif                    {                        if (!buffer.empty() && offset != 0) {                            buffer.erase(buffer.length() - offset, 1);                            offset -= 1;                        }                    }                    default:                        break;                }        // Добавить символ в буфер с учетом смещения        // при нажатии любой другой клавиши        else if (!std::count(ignore_keys.begin(), ignore_keys.end(), ch)) {            buffer.insert(buffer.end() - offset, (char)ch);            if (ch == SPACE) {                number = 0;            }        }    }}

В принципе, все готово. Давайте проверим наш код в деле.


Пример использования


#include <iostream>#include <string>#include "../include/autocomplete.h"int main() {    // Расположение файла конфигурации    std::string config_file_path = "../config.txt";    // Символы, с которых начинаются опциональные     // значения (необязательный параметр)    std::string optional_brackets = "[";    // Возможность задать цвет    #if defined(OS_WINDOWS)        uint16_t title_color = 160; // by default 10        uint16_t predict_color = 8; // by default 8        uint16_t default_color = 7; // by default 7    #elif defined(OS_POSIX)        // Set the value that goes between \033 and m ( \033{your_value}m )        std::string title_color = "0;30;102";  // by default 92        std::string predict_color = "90";      // by default 90        std::string default_color = "0";       // by default 90    #endif    // Перменная для заголовка строки    size_t command_counter = 0;    // Получаем словарь    auto [dict, status, message] = parse_config_file(config_file_path);    // Если получение словаря успешно    if (status) {        std::cerr << "Attention! Please run the executable file only" << std::endl                  << "through the command line!\n\n";        std::cerr << "- To switch the prompts press UP or DOWN arrow." << std::endl;        std::cerr << "- To move cursor press LEFT or RIGHT arrow." << std::endl;        std::cerr << "- To edit input press DELETE or BACKSPACE key." << std::endl;        std::cerr << "- To apply current prompt press TAB key.\n\n";        // Начинаем слушать        while (true) {            // Заготавливаем заголовок строки            std::string line_title = "git [" + std::to_string(command_counter) + "]:";            // Ожидаем ввода пользователя с отображением подсказок            std::string command = input(dict, line_title, optional_brackets,                                        title_color, predict_color, default_color);            // Делаем что-нибудь с полученной строкой            std::cout << std::endl << command << std::endl << std::endl;            command_counter++;        }    }    // Вывод сообщения, если файл конфигурации не был считан    else {        std::cerr << message << std::endl;    }    return 0;}



Код был проверен на macOS, Linux, Windows. Все работает отлично.


Заключение:


Как вы видите, писать кроссплатформенный код довольно не просто (в нашем случае пришлось писать, то что есть на Windows из коробки для Linux вручную и наоборот), однако это очень интересно и сам факт, что это все работает на всех трех ОС, крайне доставляет.


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


Исходный код можно взять тут.
Пользуйтесь на здоровье.

Подробнее..

Как приручить консоль, или 5 шагов к жизни с командной строкой

25.01.2021 16:17:27 | Автор: admin

Всем привет! Меня зовут Осип, я Android-разработчик в Redmadrobot и я люблю автоматизировать всё, что автоматизируется. В этом мне помогает консоль, поэтому решил поделиться опытом, как настроить командную оболочку так, чтобы в ней было приятно работать и она ежедневно помогала вам решать задачи.

Статья для тех, кто использует Linux или macOS. Если у вас Windows, вы можете использовать WSL (приравнивается к Ubuntu).

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

  • посчитать количество строк кода в проекте,

  • скопировать все файлы с расширением .png из одной папки в другую,

  • постучаться API и посмотреть какой ответ он выдаёт.

По опыту знаю: когда возникает одна из таких задач, разбираться, как это сделать в командной строке, если не используешь её это последнее, чего хочется. Кажется, что нет смысла залезать в неё ради одной задачи. Проще найти плагин или утилиту.

Поверьте, стоит один раз настроить командную оболочку и разобраться, как в ней работать, и рабочее окружение начнёт помогать вам как хорошая IDE.

Обо всём по порядку, начнём с терминологии и пойдем дальше. Если вы уже знакомы с основами, можно переходить к разделу Изучаем арсенал.


Статья только началась, а по тексту уже встречались и командная строка, и командная оболочка. Чем отличаются консоль, терминал, командная оболочка и командная строка?

Если объяснять из центра наружу: командная строка строка, где пользователь пишет команды; командная оболочка программа, которая интерпретирует команды, введённые в командную строку и выводит результат.

Термины консоль и терминал в статье используются как синонимы и обозначают программу для ввода и вывода информации. В терминале запускается командная оболочка.

Подробнее о различиях можно почитать на Ask Ubuntu: What is the difference between Terminal, Console, Shell, and Command Line?

В статье будут встречаться примеры команд. Если по ходу прочтения вы не понимаете, что делает консольная команда, скопируйте её и вставьте в ExplainShell. Благо Роскомнадзор перестал его блокировать после разблокировки Telegram.

Зачем вообще использовать командную строку

Дисклеймер: Я не призываю использовать только командную строку и отказаться от графического интерфейса. Нужно просто учитывать командную строку при выборе инструмента для решения задачи.

Когда хотят рассказать чем же хорош CLI, выделяют разные преимущества перед GUI:

  • Доступность. Командная строка доступна везде. Внутри Android Studio есть вкладка с командной строкой. Можно и вовсе настроить drop-down терминал (ещё его называют quake style), который будет появляться поверх всех приложений по нажатию сочетания клавиш.

  • Многофункциональность. Одна точка доступа к любым утилитам.

  • Простота. Простой командный интерфейс может заменить сложный графический интерфейс. Сделать графический интерфейс который позволит ввести, например, пять параметров может быть довольно нетривиальной задачей, а сделать то же самое с командным интерфейсом просто.

  • Легковесность. Как правило, CLI утилиты используют меньше ресурсов.

Меня как разработчика больше всего впечатляет, как можно комбинировать CLI утилиты. Текст интерфейс общения, который понятен для всех утилит с командным интерфейсом. Утилиты принимают на вход текст и возвращают тоже текст. Это один из принципов Unix, которые сформулировал Дуглас Макилрой в 1978 году:

Пишите программы, которые делают одну вещь и делают её хорошо.


Пишите программы, которые бы работали вместе.


Пишите программы, которые поддерживают текстовые потоки, потому что это универсальный интерфейс.

Программы, которые соблюдают эти принципы, похожи на кирпичики. По отдельности они решают простую задачу, но их комбинации позволяют решать и сложные задачи.

Примеры из жизни

Я задал вопрос коллегам-роботам: Для чего вы чаще всего открываете терминал? Получился такой ТОП-5:

  1. Работа с Git там, где не хватает графического интерфейса.

  2. Установка пакетов и управление зависимостями (подробнее про менеджер пакетов поговорим в разделе Устанавливаем менеджер пакетов).

  3. Работа с SSH.

  4. Проверка API с помощью curl.

  5. Когда нужно грохнуть процесс.

Есть и менее очевидные применения:

  1. Скачать видео из YouTube поможет youtube-dl. Качаете подкаст и нужна только аудио-дорожка? Добавьте к команде флаг --audio. Хотите скачать весь плейлист или даже весь канал? Подставляйте ссылку на канал и готовьте побольше свободного места.

  2. Хотите посмотреть отличия между файлами? Выполните команду diff и укажите пути до файлов, которые надо сравнить.

Шаг 1: Открываем терминал

Не терминал, а эмулятор терминала. (c) Департамент зануд

Обычно в ОС уже есть какой-то терминал, и для начала можно использовать его. Если хочется больше функций и возможностей кастомизации, то есть из чего выбирать. Некоторые терминалы позволяют открывать вкладки, разделять экран и даже рендерить картинки. Можно найти на любой вкус, например, стилизованный под старый ламповый монитор.

cool-retro-terminalcool-retro-terminal

Выбор терминала это тема для отдельной статьи. Кратко: если у вас Linux, начните с этого списка. На macOS популярен iTerm2, но я его не использовал, поэтому не могу ни поругать, ни похвалить.

Для меня важно чтобы и на компьютере с Linux, и на рабочем ноутбуке с macOS был один и тот же терминал с одинаковыми настройками. Я выбирал среди кроссплатформенных и остановился на kitty.

Шаг 2: Устанавливаем менеджер пакетов

Мы оказались в терминале. Первое, с чем нужно разобраться, менеджер пакетов.

Я встречал людей, которые уверены, что, для того чтобы установить программу из консоли, нужно непременно собирать её из исходников и вообще это сложно. Это не так. Программы в собранном виде хранятся в репозиториях. Пакетные менеджеры устанавливают программы из репозиториев и отвечают за их обновление и удаление.

Это даже проще, чем искать надёжный источник, из которого можно скачать программу, и удобнее, чем магазины приложений в macOS или Windows, где зачастую нет нужных программ.

Менеджеры пакетов в Linux

В дистрибутивах Linux менеджер пакетов есть по умолчанию. В Ubuntu, Debian и Mint это apt-get, а в Manjaro и ArchLinux pacman.

Чтобы установить пакет достаточно в терминале написать apt-get install [package]. Если у вас pacman, то pacman -S [package]. Может понадобиться sudo в начале, чтобы выполнить команду с правами root.

Чтобы обновить все пакеты с помощью apt-get введите команду apt-get update && apt-get upgrade. В pacman pacman -Syu.

В pacman много флагов и сложно сразу запомнить нужные. Ещё одно неудобство он не поддерживает установку пакетов из репозитория AUR. Это репозиторий, в который могут загружать пакеты любые пользователи. Исправить минусы помогут утилиты, которые упрощают работу с pacman. Рекомендую попробовать yay.

Менеджеры пакетов в macOS

В macOS придется установить пакетный менеджер. Самые популярные Homebrew и MacPorts. Homebrew активнее поддерживается сообществом, а пакеты в нём обновляются чаще, поэтому лучше использовать его. Для установки скопируйте актуальную команду установки c официального сайта. Эта команда скачает скрипт установки и запустит его.

Может понадобиться установка XCode Command Line Tools. Это базовый набор консольных инструментов clang, git, make и других. Он не зависит от XCode, а называется так, потому что необходим XCode для компиляции.

Теперь, чтобы установить пакет, достаточно в терминале написать brew install [package].

Обновляются все пакеты одной командой brew upgrade. Если brew отказывается работать, напишите brew doctor , и brew подскажет, что с ним не так, а также как это исправить.

Шаг 3: Устанавливаем командную оболочку

Чтобы было проще работать, командная оболочка должна вам помогать. В ней должен быть поиск по введённым ранее командам, а ещё должно работать автодополнение. Здорово, если будут подсказки, которые учитывают, какие команды вы используете чаще других. Не помешает подсветка синтаксиса и ошибок при вводе команд.

Все примеры в статье будут сделаны с использованием командной оболочки fish. Эта командная оболочка проста для освоения и в ней все нужные функции работают из коробки.

Чтобы узнать, какая оболочка используется по умолчанию у вас, выполните команду echo $SHELL. Скорее всего, команда выведет /env/bash или /env/zsh это самые популярные оболочки. Если хотите сравнить возможности bash, zsh и fish, посмотрите эту таблицу.

Установим fish c помощью менеджера пакетов:

# Если pacmansudo pacman -S fish# Если apt-getsudo apt-get install fish# Если brewbrew install fish

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

Fish установлен. Запускаем его командой fish:

osip@homepc ~ % fishWelcome to fish, the friendly interactive shellType `help` for instructions on how to use fishosip@homepc ~>

Теперь можно настроить внешний вид. Команда fish_config откроет в браузере страницу настроек. Здесь можно настроить цветовую схему, приглашение командной строки (prompt), посмотреть список функций, переменные окружения, историю команд и список горячих клавиш. Здесь же можно добавлять, редактировать и удалять аббревиатуры команд. Про аббревиатуры поговорим чуть позже.

Fish по умолчанию

Закройте терминал и откройте его заново. Вы окажетесь не в fish, а в командной оболочке по умолчанию. Есть два варианта, как сделать так, чтобы открывался fish:

  1. Назначить fish командной оболочкой по умолчанию.

    Нужно учитывать, что скрипты инициализации текущей командной оболочки не будут выполняться. Команды и переменные окружения из .bashrc, .bash_profile, .zshrc и т.д, нужно переместить в .config/fish/fish.config , а затем адаптировать под синтаксис fish.

  2. Использовать fish только как интерактивную оболочку.

    Это более безболезненный способ, потому что не нужно мигрировать скрипты и переменные окружения. В конце скрипта инициализации текущей командной оболочки нужно запустить fish. Добавьте строку exec fish в файл .bash_profile, если у вас bash или в .zshrc, если zsh. Эти файлы находятся в корневой директории пользователя.

    На ArchWIki есть более подробное описание этого и еще нескольких способов.

Поиск по истории

Давайте-ка посмотрим, что умеет fish. Если еще не установили, можно попробовать в браузере. Я изменил только цвета и prompt, больше ничего не настраивал.

Когда вы начинаете набирать команду, fish подсказывает команды и аргументы, которые вы использовали раньше. Чтобы применить подсказку нажмите . Подставить одно слово из подсказки Ctrl+.

sample_command это демонстрационная функция. Она принимает любые параметры и ничего не возвращает.

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

Автодополнение

Начните писать любую команду и нажмите Tab, не дописывая её до конца. Попробуйте с командой git config:

Дополнение работает с командами, подкомандами и аргументами. Вместе с вариантами дополнения выводится описание подкоманд из документации.

Если утилита не поддерживает автодополнение, fish умеет создавать дополнения из документации man. Для этого нужно выполнить команду fish_update_completions.

А что с путями? Например, хотим перейти в папку dev/tools/jarjar/:

Дополнение путей тоже работает на Tab. Для перехода по пути не обязательно писать команду cd в начале. А еще работает дополнение, если написать первую букву имени каждой папки в пути. Если указан несуществующий путь, он подсвечивается красным.

Сложно запомнить все нужные флаги у команд. Хочу вывести дерево файлов, но не помню, как ограничить его глубину и сделать так, чтобы вывод был цветным. Для такого случая есть Shift+Tab дополнение с поиском:

Автодополнение может сработать в самых неожиданных местах, например, так работает автодополнение для команды kill:

Убийство Android Studio на глазах у studentdУбийство Android Studio на глазах у studentd

Wildcards

В fish, как и в bash, есть поддержка wildcards. Wildcards позволяют выполнить команду для нескольких файлов.

Выводим все файлы с расширением .md в текущей папкеВыводим все файлы с расширением .md в текущей папке

* соответствует любой строке
** соответствует любой иерархии папок, то есть рекурсивно заходит во вложенные папки

Применим wildcard, чтобы скопировать все файлы apk после сборки в папку output:

  • cp build/*.apk output/ скопирует все apk из папки build.

  • cp build/**.apk output/ скопирует все apk из папки build и из всех вложенных папок. То, что надо.

Функции, алиасы и аббревиатуры

Большиство команд fish это функции. Можно писать и свои функции. Синтаксис такой:

funcion [название]    [тело функции]end

Если функцию нужно сохранить для будущих сессий, не забудьте выполнить команду funcsave [название] после того как напишете функцию и проверите, что она корректно работает.

Для часто используемых команд можно создать более короткие синонимы алиасы. В fish команда alias создаёт однострочную функцию.

Как выглядит alias?

Флаг --save указывает, что нужно автоматически вызвать команду funcsave после создания алиаса. Таким образом алиасы сохранятся для будущих сессий.

Другой вариант сокращения команд аббревиатуры. Они настраиваются командой abbr или в fish_config во вкладке Abbreviations.

Удобно создавать аббревиатуру одновременно в английской и русской раскладках (см. скриншот). Такая аббревиатура выполнится, даже если вы забыли переключить раскладку клавиатуры.

Аббревиатуры подставляются на лету, когда вы нажимаете Space или Enter. В отличие от алиасов, аббревиатуры не являются функциями.

И па и gf превращается в git fetchИ па и gf превращается в git fetch

Шаг 4: Изучаем арсенал

Командная оболочка есть, теперь нужны команды.

Консольные утилиты могут быть с CLI и TUI. Command Line Interface (CLI) программа принимает команды через командную строку. Так работает большинство утилит. Text User Interface (TUI) интерфейс рисуется псевдографикой и по нему можно кликать мышкой как по GUI.

TUI для SpotifyTUI для Spotify

CLI не нужно бояться. На замену старым утилитам появляются новые с улучшенным UX, ориентированные на человека, а не на скрипты.

Например, многие старые утилиты, которые выводят размер файла, по умолчанию выводят его в байтах. А утилита df вообще выводит свободное место на диске в количестве блоков по 512 байт.

Чтобы выводились понятные человеку размерности, нужно добавить флаг -h (human readable). Цветной вывод удобнее читать, но он тоже по умолчанию обычно отключен и включается добавлением флага, чаще всего -C. В современных же утилитах по умолчанию включен цветной человекопонятный вывод.

Стандартные команды

Чтобы пользоваться командной строкой, нужно знать несколько стандартных команд:

  • cd [сhange directory] команда для навигации по файловой системе. Если запустить её без аргументов, вы окажетесь в домашней папке;

  • cp [copy], mv [move], rm [remove] команды для копирования, перемещения и удаления файлов, соответственно;

  • mkdir [make directory] команда для создания папки;

  • echo выводит строку, которую ей передали.

Если команда долго работает и вы не хотите дожидаться её завершения, прервите её выполнение сочетанием клавиш Ctrl + C.

Помощь: man, help, tldr

Есть несколько способов получить справку по команде.

man выводит полную справку:

  • описание команды,

  • список аргументов и описание каждого из них,

  • какие переменные окружения использует утилита и для чего,

  • известные баги,

  • советы и примеры использования,

  • другая информация, которую посчитал полезной разработчик.

Если ввести man man, вы получите справку по команде man, где всё это подробно описано.

man это утилита с TUI, в ней есть горячие клавиши. Для поиска нажмите /, а для выхода q. / и q стандартные клавиши для поиска и выхода, они работают во многих TUI утилитах. Ещё один стандартная клавиша ?, она открывает справку.

Можно выполнить команду из man для этого нажмите ! и введите команду. Хотите открыть man для другой команды внутри man или сразу попробовать выполнить команду, следуя документации? Легко.

Страницы в man пишут разработчики утилит. Если разработчик не написал справку, man выдаст No manual entry for [command]. Но даже если нет страницы в man можно вывести краткую справку с помощью флага --help. Попробуйте написать man --help.

Для команд fish можно открыть справку в браузере командой help <command>.

Если читать мануалы некогда, то поможет утилита tldr. Она отображает типичные случаи использования команд:

tldr tldrtldr tldr

Объединяем команды

Помните, в самом начале я говорил, что консольные команды как кирпичики и их можно объединять, чтобы решить более сложную задачу? Сейчас мы этим и займёмся.

Чтобы направить вывод одной команды на вход другой, используется оператор |. Он называется pipe, а на русский его переводят как конвейер. Если мы хотим подать вывод команды find_bone на вход команде eat, нужно между этими командами поставить трубу (pipe):

$ find_bone | eat

Синтаксис понятен, но таких команд не существует и запустить их у себя в консоли не получится. Давайте посмотрим на более реальный пример:

$ echo -e "spot\\nhandle\\npick\\natlas" > robots.txt$ cat robots.txt | sortatlashandlepickspot

Оператор | нам уже знаком, но что делает >? Этот оператор направляет вывод команды в файл. После этого командой cat мы достаём содержимое файла и с помощью оператора | отдаём на сортировку.

Современные утилиты

Просмотр списка файлов: ls, tree exa

Для просмотра списка файлов в папке обычно используют стандартную команду ls, а для просмотра иерархии папках tree. Обе эти утилиты можно заменить более дружелюбной утилитой exa.

В exa по умолчанию включен цветной вывод и человекопонятные размеры файлов, есть поддержка широких экранов, отображается статус файлов в Git, если использовать команду в репозитории. Подробнее обо всех фичах читайте на сайте утилиты.

Скриншоты. Сравнение ls, tree и exa.
Сравнение вывода ls и exaСравнение вывода ls и exaСравнение вывода tree и exaСравнение вывода tree и exa

Бонус: В exa можно совместить два режима вывода.

Просмотр запущенных процессов: top htop

top и htop. Обе утилиты выводят список запущенных процессов, но htop делает это гораздо приятнее.

В htop можно сортировать процессы по разным столбцам, нажимая на них. Кнопки в нижнем меню тоже нажимаются. Столбцы и информационные блоки в шапке настраиваются можно убрать лишнее и добавить нужное.

А как выглядит top?

Работа с JSON: jq

jq незаменимая утилита, если вы работаете с JSON. Проще показать на примерах что умеет делать jq.

Валидируем json:

$ echo '{"model": spot}' | jq typeparse error: Invalid numeric literal at line 1, column 15$ echo '{"model": "spot"}' | jq type"object"

Форматируем json:

$ echo '{"model":"spot"," type":"robodog"}' | jq{  "model": "spot",  "type": "robodog"}

Выкусываем из json'а только то, что нужно:

$ set json '[{"model": "spot", "type": "robodog"}, {"model": "atlas", "type": "humanoid"}]'$ echo $json | jq 'map(.model)' --compact-output["spot","atlas"]$ echo $json | jq .[0].model"spot"# А теперь пример посложнее$ echo $json | jq 'map({(.model): .type}) | add'{  "spot": "robodog",  "atlas": "humanoid"}

Это только малая часть возможностей. Все возможности смотрите в доке.

Другие утилиты

Чтобы этот раздел не раздулся, остальные утилиты я спрятал под спойлеры. При первом прочтении их можно пропустить и позже использовать этот раздел как справочник.

Консольный HTTP клиент: curl, wget httpie

httpie делает то же что curl отправляет запросы в сеть. Но посмотрите как отличается синтаксис и оформление вывода в curl и httpie.

На фотографии слева направо: curl и httpieНа фотографии слева направо: curl и httpie
Отображение содержимого файла: cat bat

cat и bat выводят содержимое файла, но bat подсвечивает синтаксис и отображает изменения из гита.

Поиск по содержимому файлов: grep ripgrep

ripgrep более быстрая версия grep. Сравнение скорости работы показывает, что ripgrep быстрее всех :)

Поиск файлов: find fd, fzf

Для поиска файлов можно использовать стандартную утилиту find. Для неё есть альтернатива fd. Она работает быстрее, поддерживает цветной вывод, по умолчанию игнорирует скрытые файлы и файлы из .gitignore. Посмотрите на гифку, которая демонстрирует работу fd.

Ещё одна утилита для поиска fzf [fuzzy finder]. Это утилита с TUI для интерактивного поиска файлов с использованием нечёткого поиска по названиям.

Ещё из приятного есть предпросмотр содержимого.

Подсчёт количества строк кода: wc tokei

Стандартная утилита wc [word count] считает количество слов, символов и строк в файлах, но чтобы с помощью неё посчитать количество строк кода в проекте, придётся написать что-то такое:

$ fd -g '*.kt' | xargs wc -l

У такой команды есть сразу несколько недостатков:

  • считаются все строки, включая комментарии и пустые строки,

  • ищутся только файлы с расширением .kt, для поиска других придётся менять команду,

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

  • такую команду долго писать.

Утилита tokei лишена перечисленных недостатков. Вот пример вывода tokei на одном из наших проектов:

Упс, файлы proguard засчитались в пользу PrologУпс, файлы proguard засчитались в пользу Prolog
Свободное место на диске: du ncdu

Ещё один пример разницы CLI и TUI. В отличие от du, ncdu это TUI. Тут же можно перейти внутрь папки или удалить ненужное нажав d.

Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.Хм, накопилось много врапперов и кэшей Gradle. Можно почистить.
Сравнение файлов: diff delta

Отличная замена старому-доброму diff - delta. Можно использовать режим отображения side-by-side, если больше нравится, включить отображение номеров строк. Даже без дополнительных настроек диффы выглядят приятно:

Измерение времени работы программы: time hyperfine

Не верьте на слово, если я говорю, что одна утилита работает быстрее другой. Лучше проверьте.

Можно измерить время выполнения команды с помощью time (в macOS gtime). Эта утилита не предназначена для бенчмарков нет возможности прогрева, команда выполняется один раз. hyperfine подойдёт лучше, потому что изначально разработан для бенчмарков.

Попробуем замерить время выполнения команды tree:

Вывод команды tree перенаправлен в пустоту (/dev/null), потому что здесь не важен вывод команды, важно только время её выполнения. С hyperfine этого делать не нужно, он сам отбрасывает вывод команды.

Hyperfine умеет отслеживать признаки неправильного бенчмарка. Вот некоторые подсказки:

  • если во время замера есть выбросы, hyperfine посоветует закрыть другие программы, потому что они могут влиять на бенчмарк,

  • если первый запуск команды дольше остальных, hyperfine посоветует сделать прогрев, задав параметр --warmup N. Перед бенчмарком программа выполнится N раз.

Можно сравнивать скорость выполнения нескольких команд. Для этого нужно написать команды друг за другом, заключив их в кавычки:

$ hyperfine 'command_one' 'command_two' 'command_three'

Шаг 5: Сохраняем настройки

Чтобы не настраивать каждый раз все программы заново, нужно где-то сохранять настройки.

Конфиги это файлы. Обычно они хранятся в корневой директории пользователя вместе со скриптами инициализации командной оболочки, например, в папке .config/. Если вы установили fish, то найдёте папку .config/fish/ и в ней файлы с настройками. Самый простой способ сохранить конфиги сохранить их в Git-репозиторий.

Имена файлов и папок с настройками обычно начинаются с точки, поэтому одним словом их называют dotfiles. На момент написания статьи на GitHub опубликовано 138 425 репозиториев с именем dotfiles есть куда подсмотреть.

На странице awesome-dotfiles вы найдёте много информации про dotfiles. Там же есть ссылки на инструменты, которые помогают управлять dotfiles.

Я использую yadm. Мне важна кроссплатформенность, поэтому пригождается его возможность создавать альтернативные версии файлов для разных ОС.

Заключение

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

Чтобы не настраивать рабочее окружение на каждом новом компьютере заново, сохраняйте конфиги в Git-репозиторий. Но не забывайте о безопасности следите, чтобы в репозиторий не попал ключи и пароли.

На замену старым утилитам приходят новые, нацеленные на пользователя, а не на скрипты. Используйте их и ищите новые! Если вам кажется, что утилита слишком сложная, возможно, вы такой не один и кто-то уже написал более простой в использовании аналог.

И самое главное читайте документацию и исследуйте. Порой интересные возможности обнаруживаются у, казалось бы, полностью знакомых утилит.

Если будут вопросы или вам понадобится помощь с освоением консоли, пишите мне в Telegram@osipxd. Ещё я иногда пишу в канал @rareilly заметки про Android и вообще про всё интересное, что нахожу. Спасибо за внимание!

Что ещё почитать

Подробнее..

Использование быстрых клавиш в командной строке Linux (BASH)

24.02.2021 10:16:44 | Автор: admin

Эта статья посвящена наиболее часто используемым комбинациям клавиш при работе в командной строке Linux (в основном в командном интерпретаторе bash).

Она точно будет полезна начинающим своё знакомство с Linux и, уверен, пригодится тем, кто уже имеет опыт (не всегда годы практики учат работать быстрее).

Никогда не развивал навыка быстрой печати, но знание не одного десятка hotkey'ев, перечисленных в этом материале, позволяет набирать команды со скоростью мысли.

Я попытался продемонстрировать многие примеры при помощи анимированных gif'ок иногда несколько кадров больше скажут, чем несколько абзацев текста.


Материал был обкатан на вебинаре, и оттуда взяты все примеры (под каждым примером указано время, когда об этом рассказывалось в видео). Видео больше часа и без монтажных склеек, в статье же вынесены все главные моменты и попытка дать более точные определения.

Общие слова и замечания

Большинство продемонстрированных клавиш стандартны для командной строки Linux, но часть из этих комбинаций специфичны для bash (поэтому и пометил это в заголовке). На текущий момент BASH наиболее распространенный и используемый по умолчанию командный интерпретатор в большинстве Linux-дистрибутивах. В других командных интерпретаторах или, проще говоря, shell'ах (рекомендую попробовать zsh и fish) могут быть небольшие отличия в работе. Также часть комбинаций прописана в настройках по умолчанию (например, в файле /etc/inputrc или в /etc/bashrc), которые тоже могут различаться в разных дистрибутивах. И бывает, что некоторые клавиши могут быть настроены и перехватываться графической оболочкой, в которой запущен командный интерпретатор.

Если вдруг что-то из указанного мной в этой статье у вас не сработало, пишите в комментариях название - версию shell и название - версию дистрибутива.

Часть демонстрируемых клавиш относятся к настройкам терминала. А часть клавиши из командного интерпретатора BASH, и их можно посмотреть, почитав мануал по bash'у (огромный текст пользуйтесь поиском):

man bash

[00:10:40][00:10:40]

^^^ На приведенном фрагменте из мануала: Запись (C-r) означает Ctrl-r, а M-> означает Alt->.

M - это Meta-клавиша

Из истории: М - это Metа-клавиша, сейчас это клавиша Alt, либо также можно вместо неё использовать Esc. Я чаще в её качестве буду использовать клавишу Alt.

Замечание: Если у кого-то настроено переключение раскладки по комбинации Alt-Shift, то учитывайте, что в комбинациях, перечисленных далее и содержащих Alt-Shift, вам надо будет использовать скорее клавишу Esc-Shift (или поменять комбинацию для переключения раскладки клавиатуры, у меня, например, раскладка переключается по CapsLock).

[00:11:18][00:11:18]

^^^ Здесь показано на примере использования комбинаций для перемещения по словам командной строки: Alt-b(или, что то же самое, Esc-b) и Alt-f(или, что то же самое, Esc-f)

Для демонстраций нажатых клавиш использую утилиту screenkey.

Терминал

Итак, начнем с клавиш из настроек терминала. Их можно посмотреть, выполнив команду:
stty -a

А перенастроить, например, так:
stty intr НоваяКлавиша

Ctrl-c сигнал SIGINT

Первые две комбинации клавиш достаточно важные, и часто недавно перешедшие с Windows на Linux НЕправильно их используют: продолжая, как в DOS, для завершения команд использовать комбинацию Ctrl-z, что неверно. В Linux же для того, чтобы попросить (команде посылается сигнал SIGINT) приложение прервать свою работу, используется Ctrl-c.

[00:14:24][00:14:24]

Ctrl-z сигнал SIGTSTP

А комбинация Ctrl-z используется, чтобы попросить (команде посылается сигнал SIGTSTP) приложение остановить свою работу (не завершая) поставить на паузу. Ну, а разбудить его можно командой fg (или bg).

[00:14:36][00:14:36]

Ctrl-d EOF(окончание ввода данных)

Далее разберем комбинацию Ctrl-d. В выводе stty -a эта комбинация значится как EOF, что означает окончание ввода данных. Для примера покажу, как можно создать текстовый файл с определенным текстом без использования текстового редактора:

[00:15:51][00:15:51]

^^^ Здесь видно, что любой набираемый текст перенаправляется в файл /tmp/File.txt, и нет никакой фразы (типа Горшочек, не вари), которую бы команда cat восприняла как окончание ввода точнее, для этого как раз и надо нажать Ctrl-d.

Также пользуюсь комбинаций Ctrl-d для того, чтобы выйти из консоли (например, после того как переключился командой su под другого пользователя или зайдя по ssh на другую машину) вместо набора команды exit или logout:

[00:17:44][00:17:44]

^^^ В правом терминале отображаю историю команд.

...(а внимательный зритель догадается, какой супер-секретный пароль у root'а на стенде)

Ctrl-v ввод следующего символа

Комбинация Ctrl-v позволяет вставить в командную строку следующий за ней символ, не воспринимая его как спецсимвол. (Параметр lnext в выводе stty -a)

[00:19:19][00:19:19]

^^^ Здесь для примера показываю, как в скрипте выводить строку текста с использованием табуляции (знаю, что можно использовать \t в команде echo: echo -e "\tTEXT", но не у всех утилит есть такая возможность, а подобная необходимость вставить спецсимвол случается).

[00:20:38][00:20:38]

^^^ А в этом примере у меня есть файл New Text Document.txt, созданный в ОС Windows при помощи программы Notepad в директории, которую я затем открыл на доступ по сети и примонтировал в Linux в директорию /mnt. Программа Notepad (в отличии от Notepad++) создает файл в DOS-формате в конце каждой строки использует дополнительный символ Возврат каретки. Терминалы часто по умолчанию этот символ не отображают, но он есть, и поэтому, например, команда 'grep "m$" /mnt/New\ Text\ Document.txt' не выведет строку, заканчивающуюся на букву m. Команда cat с опцией -v отображает этот символ. А для того, чтобы при выводе заменить или удалить это символ, воспользовался командой tr (хотя можно было бы использовать специальную для этого утилиту unix2dos).

Ctrl-l очищает экран

Комбинация Ctrl-l очищает экран.

[00:10:51][00:10:51]

История команд

Работа с историей команд классическая тема обычно из любого начального курса по Linux (по крайней мере, среди тех курсов, которые читаются у нас в Сетевой Академии ЛАНИТ). И многие, кто имеет хотя бы небольшой опыт работы с командной строкой, историей команд пользуются как минимум знают, что она есть, и используют стрелки вверх (отобразить предыдущую команду) и вниз (отобразить следующую после отображаемой команду в истории команд), чтобы выбрать, какую из ранее введенных команд либо снова выполнить, либо подредактировать и запустить отредактированную. Но помимо стрелок еще есть ряд полезных комбинаций клавиш, которые позволяют работать с историей команд, быстрее находить нужные команды.

Ctrl-r Поиск по истории

Комбинация Ctrl-r позволяет искать в истории команд команды, содержащие указанный далее текст.

[00:25:21][00:25:21]

^^^ В этом примере мне понадобилось из истории вытащить команду, содержащую текст su: нажав Ctrl-r и набрав искомый текст su, я увидел самую недавнюю команду, содержащую su; при повторном нажатии Ctrl-r отображается предыдущая команда, содержащая su и т.д. При необходимости изменить команду жму стрелку вправо и правлю текст, а чтобы запустить команду - нажимаю Enter.

PgUp/PgDown Поиск по истории

PgUp отображает предыдущую команду начинающуюся с уже введенного текста, PgDown следующую.

[00:27:35][00:27:35]

^^^ В этом примере перемещаюсь между командами, начинающимися с cat. (Часто также ищу команды, начинающиеся с sudo. Или если мне нужно снова отредактировать какой-то файл, который недавно редактировал: набираю vi, жму несколько раз PgUp, а затем Enter.)

В дистрибутивах где это настроено - в /etc/inputrc есть строки:
"\e[5~":history-search-backward"\e[6~":history-search-forward

Alt-_/Alt-./Alt-- вставка аргументов

Комбинация Alt-_ (выполняется нажатием Alt, Shift, -) вставляет последний аргумент из предыдущих команд. (Аналогично работает комбинация Esc-. или, что то же самое, Alt-.)

[00:28:32][00:28:32]

^^^ В данном примере видно, как повторные нажатия Alt-_ вставляют аргументы от пред-пред--идущих команд.

Комбинация Alt - позволяет указать (порядковый номер с конца), какой аргумент вставить клавишей Alt-_ из предыдущей команды.

[00:30:13][00:30:13]

^^^ В данном примере вставляю в командную строку различные аргументы из предыдущей команды.

Alt-# текущую команду преобразовать в комментарий

Бывает, во время набора очень длинной команды понимаю, что мне нужно что-нибудь посмотреть или дополнительно сделать (например, глянуть, какие файлы есть в определенной директории, прочитать мануал по команде, установить нужный пакет). Что делать с уже набранным текстом? Хотелось бы посмотреть нужную информацию и продолжить набирать команду, а не начинать печатать её сначала. Alt-# (выполняется нажатием Alt, Shift, 3. Также можно использовать Esc-#) преобразует текущую набранную команду в комментарий в истории добавляет символ # в начало строки и добавляет полученную строку в историю команд.

[00:32:03][00:32:03]

Ctrl-o повтор команд из истории

Комбинация Ctrl-o позволяет повторять серию команд из истории. То есть нужно из истории команд стрелками выбрать первую команду из серии и нажать Ctrl-o это выполнит текущую команду и выведет из истории следующую. Дальше можно продолжать нажимать Ctrl-o с тем же эффектом.

[00:33:58][00:33:58]

^^^ В примере я написал три команды: одна увеличивает на 1 переменную, которой соответствует год; вторая выводит переменную-год; третья показывает, сколько дней в феврале в указанном году. Дальше, нажимая Ctrl-o, повторяю эту серию из трех команд много раз (один кадр соответствует трем нажатиям).

Автодополнение

Tab автодополнение (в контексте)

Во многих командных интерпретаторах (и в bash в том числе) используется такая возможность, как автодополнение. Как минимум нужно знать, что по нажатию клавиши Tab дописывается название команды. В bash по умолчанию обычно настроено так, что если имеется только один вариант дополнения, то он дописывается по нажатию Tab (также можно использовать Ctrl-i и Esc-Esc). Когда вариантов дополнения много, то по первому нажатию Tab дописывается только общая часть (если она есть). А по второму нажатию Tab отображается список всех доступных вариантов. Дальше можно набрать еще символов уточнить, какое из дополнений нужно, и снова нажать Tab. То же самое с другими дополнениями: имен файлов, имен переменных.

[00:39:20][00:39:20]

^^^ Здесь, например, смотрю (нажав дважды Tab), что есть несколько команд, начинающихся с if, добавив c и нажав Tab, получаю набранной команду ifconfig.

[00:39:31][00:39:31]

^^^ В этом примере дополняю аргументы команды (здесь имена файлов). Также видно, что в случае, когда вариантов много и все не умещаются в окне терминала, их список отображается утилитой для постраничного просмотра (также при очень большом списке вариантов выдается запрос вида Display all 125 possibilities? (y or n) или, как в этом примере, при малом количестве - --More--.

Дополнения имен пользователей, переменных

Часто, когда дописываются аргументы команд по Tab, дописываются имена файлов. Но стоит также отметить, что, в зависимости от контекста, по Tab дописываются и имена переменных (аргументы, начинающиеся с символа $), имена пользователей (аргументы, начинающиеся с символа ~),

[00:40:36][00:40:36]

^^^ Здесь, чтобы набрать $HISTFILESIZE, вместо 13 символов набрал 8 символов ($ H I Tab F Tab S Tab). Помимо того, что так быстрее, это еще и позволяет допускать меньше ошибок при наборе команд, так как не просто печатаю текст, а выбираю из списка установленных переменных.

[00:41:44][00:41:44]

^^^ Здесь дописываю имена пользователей (фактически пишу адрес домашней директории).

Также bash может дополнять не потому, что набранный текст начинается с определенного символа, а по определенным комбинациям клавиш.

Список того, что может дополнять bash, можно посмотреть командой:

bind -P | grep "complet"
possible-username-completions can be found on "\C-x~".complete-username can be found on "\e~".possible-hostname-completions can be found on "\C-x@".complete-hostname can be found on "\e@".possible-variable-completions can be found on "\C-x$".complete-variable can be found on "\e$".possible-command-completions can be found on "\C-x!".complete-command can be found on "\e!".possible-filename-completions can be found on "\C-x/".complete-filename can be found on "\e/".
[00:43:50][00:43:50]

Так, например, видно, что:

  • Ctrl-x ~ покажет список имен пользователей, начинающихся с набранных символов, а дополнить комбинацией Esc-~;

  • Ctrl-x @ список имен машин (согласно /etc/hosts), начинающихся с набранных символов, а дополнить Esc-@;

  • Ctrl-x $ список имен переменных, заданных в этой сессии (можно их также посмотреть командой set), а дополнить Esc-$;

  • Ctrl-x ! список команд (согласно доступных: $PATH, alias, функций, встроенных команд), а дополнить Esc-!;

  • Ctrl-x / список имен файлов, а дополнить Esc-/.

Alt-* вставить дополнения, Ctrl-x * развернуть шаблон

Esc-* (точнее, Esc Shift 8) или, что, то же самое, Alt-* (точнее, Alt, Shift, 8), вставит все варианты дополнения в командную строку. Аналогично можно развернуть список файлов, переменных, имен пользователей.

В примерах ниже разворачиваю список файлов:

Вариант с Alt-*:

[00:44:55][00:44:55]

Вариант с Esc-*:

[00:46:30][00:46:30] bind -P | grep '*'
insert-completions can be found on "\e*".glob-expand-word can be found on "\C-x*".

Ctrl-x * развернет уже написанный в командной строке шаблон, как в примере ниже:

[00:48:39][00:48:39]

Редактирование

Ctrl-w/u/k вырезать слово/начало/конец строки

Ctrl-w вырезать слово (от текущего положения курсора до ближайшего ранее в строке пробела/табуляции). Вырезанное можно затем вставить комбинацией Ctrl-y.

[00:52:52][00:52:52]

Ctrl-u вырезать начало строки (от текущего положения курсора. Если курсор в конце строки, то вырежет целиком строку). Вырезанное можно затем вставить комбинацией Ctrl-y.

Ctrl-k вырезать конец строки (от текущего положения курсора. Если курсор в начале строки, то вырежет целиком строку). Вырезанное можно затем вставить комбинацией Ctrl-y.

Ctrl-y вставить вырезанное

Ctrl-y вставить вырезанный фрагмент командной строки. (В bash используется свой буфер для хранения вырезанных фрагментов называется kill ring).

Важно: Удобно использовать с Alt-y (позволяет прокручивать варианты вставки из буфера).

Ctrl-x Ctrl-e редактировать в $EDITOR

Нажав комбинацию Ctrl-x Ctrl-e, можно редактировать командную строку в любом внешнем редакторе (по умолчанию часто используется редактор vim; переназначить редактор можно, указав в переменной EDITOR). Часто редакторы имеют больше продвинутых возможностей в редактировании текста. Особенно удобно, если редактор умеет подкрашивать синтаксис команд и имеет различные встроенные инструменты для быстрого поиска и исправления ошибок.

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

[00:53:40][00:53:40]

Ctrl-_ undo

Ctrl-_ (точнее, нужно нажать Ctrl Shift -) или Ctrl-x Ctrl-u отменяет последние правки при редактировании командной строки.

Перемещение

Ctrl-a/e в начало/конец строки

Ctrl-a и Ctrl-e перемещение в начало и конец командной строки соответственно. Можно, конечно, пользоваться клавишами Home и End, но так быстрее при использовании, например, таких клавиш, как вырезание Ctrl-w и вставка Ctrl-y.

[00:52:05][00:52:05]

Alt-b/f и Ctrl-/ предыдущее/следующие слово

Alt-b (и тот же эффект у Ctrl-Left) переход в начало предыдущего слова.

Alt-f (и тот же эффект у Ctrl-Right) переход в конец следующего слова.

[00:50:10][00:50:10]

Настройки

bash

Подробнее значения действия редактирования командной строки bash можно посмотреть в мануал по bash'у. Действия, упомянутые в этой статье (в порядке упоминания):

man bash
  • clear-screen (C-l) Clear the screen, then redraw the current line, leaving the current line at the top of the screen.

  • reverse-search-history (C-r) Search backward starting at the current line and moving `up' through the history as necessary. This is an incremental search.

  • reverse-search-history (C-r) Search backward starting at the current line and moving `up' through the history as necessary. This is an incremental search.

  • history-search-backward Search backward through the history for the string of characters between the start of the current line and the point. This is a non-incremental search.

  • history-search-forward Search forward through the history for the string of characters between the start of the current line and the point. This is a non-incremental search.

  • yank-last-arg (M-., M-_) Insert the last argument to the previous command (the last word of the previous history entry). With a numeric argument, behave exactly like yank-nth-arg. Successive calls to yank-last-arg move back through the history list, inserting the last word (or the word specified by the argument to the first call) of each line in turn. Any numeric argument supplied to these successive calls determines the direction to move through the history. A negative argument switches the direction through the history (back or forward). The history expansion facilities are used to extract the last word, as if the "!$" history expansion had been specified.

  • digit-argument (M-0, M-1, ..., M--) Add this digit to the argument already accumulating, or start a new argument. M-- starts a negative argument.

  • insert-comment (M-#) Without a numeric argument, the value of the readline comment-begin variable is inserted at the beginning of the current line. ... The default value of comment-begin causes this command to make the current line a shell comment.

  • operate-and-get-next (C-o) Accept the current line for execution and fetch the next line relative to the current line from the history for editing. A numeric argument, if supplied, specifies the history entry to use instead of the current line.

  • complete (TAB) Attempt to perform completion on the text before point. Bash attempts completion treating the text as a variable (if the text begins with $), username (if the text begins with ~), hostname (if the text begins with @), or command (including aliases and functions) in turn. If none of these produces a match, filename completion is attempted.

  • complete-username (M-~) Attempt completion on the text before point, treating it as a username.

  • possible-username-completions (C-x ~) List the possible completions of the text before point, treating it as a username.

  • complete-hostname (M-@) Attempt completion on the text before point, treating it as a hostname.

  • possible-hostname-completions (C-x @) List the possible completions of the text before point, treating it as a hostname.

  • complete-variable (M-$) Attempt completion on the text before point, treating it as a shell variable.

  • possible-variable-completions (C-x $) List the possible completions of the text before point, treating it as a shell variable.

  • complete-command (M-!) Attempt completion on the text before point, treating it as a command name. Command completion attempts to match the text against aliases, reserved words, shell functions, shell builtins, and finally executable filenames, in that order.

  • possible-command-completions (C-x !) List the possible completions of the text before point, treating it as a command name.

  • complete-filename (M-/) Attempt filename completion on the text before point.

  • possible-filename-completions (C-x /) List the possible completions of the text before point, treating it as a filename.

  • insert-completions (M-*) Insert all completions of the text before point that would have been generated by possible-completions.

  • glob-expand-word (C-x *) The word before point is treated as a pattern for pathname expansion, and the list of matching filenames is inserted, replacing the word. If a numeric argument is supplied, an asterisk is appended before pathname expansion.

  • unix-word-rubout (C-w) Kill the word behind point, using white space as a word boundary. The killed text is saved on the kill-ring.

  • unix-line-discard (C-u) Kill backward from point to the beginning of the line. The killed text is saved on the kill-ring.

  • kill-line (C-k) Kill the text from point to the end of the line.

  • yank (C-y) Yank the top of the kill ring into the buffer at point.

  • yank-pop (M-y) Rotate the kill ring, and yank the new top. Only works following yank or yank-pop.

  • undo (C-_, C-x C-u) Incremental undo, separately remembered for each line.

  • backward-word (M-b) Move back to the start of the current or previous word. Words are composed of alphanumeric characters (letters and digits).

  • forward-word (M-f) Move forward to the end of the next word. Words are composed of alphanumeric characters (letters and digits).

bind -P

Можно посмотреть, какие клавиши к каким действиям редактирования командной строки bash привязаны для этого можно воспользоваться командой bind -P.

Есть и много других интересных комбинаций для примера можно глянуть:

Клавиши, переключающие регистр букв:
bind -P | egrep "case|capitalize"

[00:58:35][00:58:35]

Клавиши, меняющие слова/буквы местами:
bind -p | grep "transpose"

Также можно настроить свои привязки например, чтобы по комбинации Ctrl-f выводился результат команды date:

bind -x'"\C-f": date'

[01:00:50][01:00:50]

/etc/inputrc (настройки библиотеки readline)

Так как bash и многие shell'ы используют библиотеку readline для взаимодействия с командной строкой, то можно перенастроить соответствия комбинаций клавиш и действий в /etc/inputrc.

Например, такие строки меняют поведение по умолчанию клавиш Вверх и Вниз

... вместо предыдущей/последующей команды они будут включать поиск по истории команд очень удобно, часто этим пользуюсь:

"\e[A": history-search-backward"\e[B": history-search-forward

Коды клавиш можно посмотреть, используя комбинацию Ctrl-v, упомянутую выше в этой статье.

Итог

Полезных клавиш, делающих более удобной и быстрой работу в командной сроке, достаточно много, и в этой статье я перечислил только малую часть этих комбинаций клавиш. Зато наиболее часто используемые комбинации.

Если, по вашему мнению, стоило упомянуть какие-то еще полезные комбинации - напишите в комментариях.


На этом всё. Надеюсь, было полезно. Если есть какие-то вопросы и уточнения, пишите, я буду рад ответить. Также буду рад упоминаниям в комментариях, что для вас оказалось полезным/новым в этой статье. Так я пойму, что стоило упоминать, а что можно было и пропустить.

Ну, и приходите к нам учитьсяв Сетевую Академию ЛАНИТ!

P.S. Также рекомендую к прочтению мою предыдущую Habr-статью:
Как устроена графика в Linux: обзор различных сред оформления рабочего стола.


А в завершение конкурс для внимательных зрителей.
Кто первым правильно напишет в комментариях ответ на три вопроса, получит 25% скидку на курс по написанию Bash-скриптов:

  1. дата, когда проходил вебинар Сетевой Академии ЛАНИТ по теме этой статьи;

  2. какой пароль у пользователя root на системе, используемой на вебинаре?

  3. какой дистрибутив Линукс использовался?

Подробнее..

Линукс-порт Far Manager прогресс за 4 года

20.10.2020 22:18:35 | Автор: admin
Первая публикация исходников far2l, порта Far Manager под Линукс штуки, которая когда-то считалась принципиально невозможной состоялась 10 августа 2016го. Поскольку главный разработчик поговаривает о переходе проекта в статус беты, решил написать обзорный пост, как там идут дела и чего удалось добиться за прошедшие четыре года.

Консольная версия


Первый же вопрос, который все задавали и здесь, и на опеннете, и на лоре а чего не в консоли? Изначально far2l действительно работал только в графическом режиме, через wxWidgets. Это оказалось самым простым способом быстро получить работающий порт со всеми пользовательскими удобствами: иксовым буфером обмена и всеми сочетаниями клавиш, причем с поддержкой событий не только KeyDown, но и KeyUp.

Сейчас это ограничение в прошлом: far2l прекрасно себя чувствует в консоли. Более того, там появились так называемые расширения терминала far2l, поэтому если запускать консольный far2l внутри графического (например, зайдя куда-нибудь по ssh), они сконнектятсямежду собой, и внутренний far2l тоже будет видеть и буфер обмена (с разрешения пользователя, конечно), и все возможные горячие клавиши. Более того, сделана даже специальная сборка putty, позволяющая наслаждаться всеми этими фишками из Windows.

Вот, смотрите, это far2l в GNOME Terminal


А вот в putty


Русские буквы в .zip'ах с винды


Вы не поверите, но Windows, вплоть, как минимум, до семерки создавала .zip архивы, записывая туда имена файлов в OEM (DOS) кодировке! Совместимость страшная штука. В итоге StackOverflow переполнен вопросами как мне правильно распаковать зип с кракозябрами.

В far2l мы это починили. В процессе родилась демонстрационная утилитка, которая показывает, как правильно работать с кодировками в .zip'ах, чтобы кракозябров не было (пригодится авторам архиваторов), а также патчик к p7zip, применяющий аналогичный алгоритм. p7zip-с-патчиком даже запакован в .deb'ку (ubuntu 20.04+, amd64), установка которой чинит поддержку зипов, например, в Engrampa, используемом в MATE.



Приведение в порядок зависимостей


Изначально порт нёс в себе кучу кода библиотек, и так присутствующих в мире Linux в системных пакетах: pcre, minizip, universal charset detector. Сейчас всё это заменено на грамотное использование зависимостей: системные библиотеки используются везде, где это возможно (исключение: 7z и unrar, там статически компилируются самые свежие библиотеки для поддержки самых свежих особенностей форматов).

Человеческая поддержка .tar.gz


Плагин multiarc, который используется в far2l для доступа к архивам, приехал к нам из мира Windows, и не умел воспринимать .tar.gz как один архив. Он видел .gz, и внутри него .tar. Следовательно, чтобы получить список файлов, приходилось делать полную распаковку. Такой себе экспириенс. Сейчас перешли на libarchive, и эта проблема исчезла.

Красивое консольное окно


В графической версии far2l была проблема: символы рисования рамок показывались раздражающим пунктиром.

image

У нас долго не получалось найти причину или подобрать настройки рендеринга, исправляющие этот бесячий баг, пока я не заглянул в исходники терминалок из GNOME и KDE. А там, оказывается, давным-давно символы рисования рамок рендерятся не из шрифта, а вручную, чтобы линии четкие и красивые получались.

В итоге сделали так же в far2l, только немножко лучше: добавив сглаживание. А то в терминалах KDE и GNOME максимально контрастные линии слишком сильно отвлекают на себя внимание рядом со сглаженным текстом.

В процессе пришлось разобраться в психовизуальных нюансах антиалиасинга, отлавливая несуществующие слишком яркие пиксели, которые упорно видели глаза. Подробности.

image

Пакеты для дистрибутивов


В репозитории дистрибутивов мы пока не попали. Зато давно есть пакеты во всех основных форматах. Даже скрипт сборки для Amazon Linux есть! Ну и конечно есть ppa для *buntu самый удобный способ просто поставить фар для большинства пользователей.

Свежий Colorer


far2l форкнулся от ветки Far 2, так что некоторые плагины там требовали срочного обновления. Недавно такое обновление было проведено: обновили код распаковки 7z и unrar, а также схемы цветовой подсветки синтаксиса Colorer. Остальные портированные плагины не содержат каких-то регулярно обновляющихся штук, а вот старые раскраски и не открывающиеся новые архивы были реальной проблемой. Всё, её больше нет!



NetBoxRocks


Это был второй вопрос, который обычно задавали в каментах: а нетбоооокс буууудеееет?. Нет, нетбокса не будет! Там putty внутри, и тащить в far2l её linux-версию показалось странной затеей. Да и сам код netbox'а не слишком располагал к портированию.

Поэтому автор порта, великолепный elfmz, сделал свою версию сетевого плагина: NetRocks. Там есть всё, что только может понадобиться и ftp[s], и scp, и sftp, и nfs, и webdav, и даже samba! Всё работает на нативных линуксовых библиотеках. В sftp можно даже удаленные команды запускать и в удаленную консоль ходить.

А ещё в NetRocks есть псевдо-сетевой плагин file, который позволяет работать с локальной файловой системой. Зачем? Потому что NetRocks умеет в фоновые операции. А обычное копирование файлов Far не умеет.



А как там вообще с плагинами?


Помимо NetRocks есть в комплекте есть:
colorer (подсветка синтаксиса, свежий!)
multiarc (работа с архивами, доработанный, свежие архиваторы!)
tmppanel (временная панель)
align block (форматирование блоков для редактора)
autowrap (автоперенос слов в редакторе)
drawline (рисование линий в редакторе)
editcase (конвертация регистра в редакторе)
SimpleIndent (работа с отступами в редакторе)
compare (продвинутая версия сравнения папок)
editor autocomplete (автодополнение в редакторе)
filecase (конвертация регистра имен файлов)
incremental search (быстрый поиск в редакторе)
inside (показывает, что внутри ELF и некоторых других форматов)
и даже плагин для написания других плагинов на Python!

Есть парочка сторонних, far2-gvfs и far2l-fuse, но после появления NetRocks они в некоторой степени утратили актуальность.

В общем, базовый набор для комфортной работы с локальными и удаленными файлами и архивами, а также написания кода прямо в редакторе far2l имеется :)

А со стабильностью как? Когда релиз уже?


Со стабильностью всё хорошо: за 4 года использования в работе (webdev + всякое офисное) ни одной потери данных с far2l я не поймал. Автор порта готов понемножку менять статус с альфы на бету, если в течении месяца-двух не вылезет критических ошибок. Тогда можно будет подумать об отправке пакетов в репозитории дистрибутивов, например.

Bonus #1. А там правда Wine под капотом?


Отчасти :) Из Wine были вытащены некоторые кусочки трансляции WinApi в нативный API Linux (конвертация кодировок, например; к слову, этот кусок недавно попробовали переписать на iconv, но выяснилось, что код из Wine делает то же самое в 4 раза быстрее). Со временем обращения к этим функциям, разбросанные по всему коду far2l, можно будет понемножку заменять на прямые вызовы нативных функций. А пока и эта конструкция работает очень даже быстро (самое медленное, на чём пробовали запускать raspbery pi, полет нормальный) и вполне надежно.

Bonus #2. Хватит фигней страдать, консоль учите, дурни!


За время работы над far2l (тестировщиком, а ещё иногда нехитрые патчи шлю) я узнал о линуксовой консоли больше, чем за всю жизнь до этого :) А ещё понемногу осваиваю си, на которых кодить со времен института не приходилось повода не было (заодно, кстати, перешел на ты с git). Так что ждите скоро статью как веб-макака си на опен сорсе учила!

Bonus #3. А нескучные обои имеются?


Несколько лет назад я бы скорее посмеялся над этим вопросом. Но да, если целый день смотреть в синий цвет, становится как-то не по себе (и прямо чувствуешь, как отрастает борода и свитер с оленями). Поэтому в итоге сделал себе раскраску в духе Monokai, ну и выложил для всех, конечно. У нас opensource ведь :)



Bonus #4. А на маке взлетит?


Взлетит. Половину тикетов в багтрекер с маков пишут. И да, на BSD работает тоже.
Подробнее..

Запускаем Homebrew на Windows 10

08.06.2021 14:19:38 | Автор: admin

Коллеги, которые только начали погружение в мир Cloud Native, часто задаются вопросом, как установить необходимое ПО на Windows. Решение уже давно известно Windows Subsystem for Linux (WSL). Это действительно неплохой вариант для полноценной работы. И не забывайте, что установить все необходимые утилиты очень просто вам нужен Homebrew. Этот пакетный менеджер уже давно доступен не только для OS X, но и для Linux. Приступим!

Установка Ubuntu 20.04

Заходим в Microsoft Store и устанавливаем Ubuntu 20.04. Может потребоваться удалить старую версию:

CTRL + R -> CMD#Check installationC:\Users\suchak>wsl --listWindows Subsystem for Linux Distributions:Ubuntu-20.04 Legacy (Default)#Uninstalling older versionC:\Users\suchak>wsl --unregister Legacy#Check the default installation againC:\Users\suchak>wsl --listWindows Subsystem for Linux Distributions:Ubuntu-20.04 (Default)

Устанавливаем терминал для Windows

Iterm2 великолепный терминал для OS X. Работая в Linux я предпочитаю Tilix. А что же выбрать для работы с Windows? Неплохим решением может быть Terminus, хотя вы можете использовать и Power Shell.

Для установки Terminus загружаем инсталляционный файл для Windows из официального репозитория на GitHub.

Устанавливаем Homebrew

Запускаем терминал Terminus и приступаем к установке. Для работы Homebrew требуется gcc:

sudo apt install gcc

Теперь устанавливаем Homebrew:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Всё готово!

Установим необходимые программы:

brew install kind kubernetes-cli helm k9s kubectx kubecolor

Теперь мы можем без труда устанавливать дополнительные приложения, работая c Windows, как если бы мы работали с OS X или Linux.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru