Як я можу конвертувати вкладки в пробіли у кожному файлі каталогу?


251

Як я можу конвертувати вкладки у пробіли у кожному файлі каталогу (можливо, рекурсивно)?

Також чи існує спосіб встановлення кількості пробілів на вкладці?


Ви хочете замінити вкладки у файлах чи назви файлів?
cppcoder

3
prє чудовою утилітою для цього. Дивіться цю відповідь .
codeforester

Відповіді:


69

Попередження: Це порушить ваше репо.

Це призведе до пошкодження бінарних файлів , в тому числі під svn, .git! Прочитайте коментарі перед використанням!

find . -iname '*.java' -type f -exec sed -i.orig 's/\t/ /g' {} +

Оригінальний файл зберігається як [filename].orig.

Замініть "* .java" на закінчення файлу типу потрібного файлу. Таким чином ви можете запобігти випадковій пошкодженню бінарних файлів.

Недоліки:

  • Буде замінено вкладки скрізь у файлі.
  • Триватиме багато часу, якщо у вас в цьому каталозі буде 5,2 ГБ SQL-дампа.

12
для візуального простору, що є сумішшю вкладок і пробілів, такий підхід дає неправильне розширення.
піца

7
Я також додав би відповідник файлів, як, наприклад, лише для файлів .php find ./ -iname "* .php" -типу f -exec sed -i 's / \ t / / g' {} \;
Daniel Luca CleanUnicorn

98
НЕ ВИКОРИСТОВУЄТЬСЯ СЕБ! Якщо в рядку є вбудована вкладка, ви можете закінчити керування кодом. Це те, що команда розширення мала подумати. Використовуйте expand.
Девід В.

5
@DavidW. Я просто оновив би цю команду, щоб замінити лише вкладки з початку рядка. find ./ -type f -exec sed -i 's/^\t/####/g' {} \;. Але я не знав про команду розширення - дуже корисно!
Мартін Конечний

29
НЕ ВИКОРИСТОВУВАТИ! Ця відповідь також просто зруйнувала моє місцеве сховище git. Якщо у вас є файли, що містять змішані вкладки та пробіли, вони будуть вставляти послідовності #. Використовуйте відповідь Гена або коментар Дожа нижче.
ляльковий

344

Проста заміна на sedце добре, але не найкраще можливе рішення. Якщо між вкладками є "зайві" пробіли, вони після заміни залишаться там, тому поля будуть нерівними. Вкладки, розширені посередині рядків, також не працюватимуть правильно. В bash, ми можемо сказати замість цього

find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;

застосувати expandдо кожного файлу Java у поточному дереві каталогів. Видаліть / замініть -nameаргумент, якщо ви орієнтуєтесь на деякі інші типи файлів. Як зазначається в одному з коментарів, будьте дуже обережні, видаляючи -nameабо використовуючи слабку, підстановку. Ви можете легко клобувати сховище та інші приховані файли без наміру. Ось чому оригінальна відповідь включала це:

Ви завжди повинні робити резервну копію дерева, перш ніж спробувати щось подібне, якщо щось піде не так.


2
@JeffreyMartinez Чудове запитання. gniourf_gniourf відредагував мою оригінальну відповідь 11 листопада і зробив зневажливі зауваження щодо невідомого правильного способу використання {}. Схоже, він не знав про те, $0коли -cйого використовують. Потім dimo414 змінився від мого використання temp в каталозі перетворення на /tmp, що буде набагато повільніше, якщо він /tmpзнаходиться в іншій точці монтування. На жаль, у мене немає вікна Linux для тестування вашої $0пропозиції. Але я думаю, ти маєш рацію.
Гена

1
@Gene, дякую за уточнення, це звучить як stackoverflow добре: p. Хоча я на цьому, але додам, що для правильного виходу з * .java я мав використовувати лапки навколо '* .java'.
Джефрі Мартінес

2
Якщо у когось є помилка «невідомого основного або операторського» помилки пошуку, то ось ця повна команда, яка виправить це:find . -name '*.java' ! -type d -exec bash -c 'expand -t 4 "$0" > /tmp/e && mv /tmp/e "$0"' {} \;
Doge

4
Я вважав, що ця відповідь не мала достатньо коментарів, як це було, тому це моє: якщо використовувати використання spongeвід joeyh.name/code/moreutils , ви можете написатиfind . -name '*.py' ! -type d -exec bash -c 'expand -t 8 "$0" | sponge "$0"' {} \;
tokland

8
Не будьте дурними і не використовуйте find . -name '*', я щойно знищив своє місцеве git repo
Gautam

193

Спробуйте інструмент командного рядка expand.

expand -i -t 4 input | sponge output

де

  • -i використовується для розширення лише провідних вкладок у кожному рядку;
  • -t 4 означає, що кожна вкладка буде перетворена на 4 символи пробілу (8 за замовчуванням).
  • sponge є від moreutils пакеті і уникає очищення вхідного файлу .

Нарешті, ви можете використовувати gexpandна OSX після встановлення coreutilsз Homebrew ( brew install coreutils).


5
Це одна з GNU_Core_Utilities
кев

32
Вам слід перейти -iдо expandзаміни лише провідних вкладок у кожному рядку. Це допомагає уникнути заміни вкладок, які можуть бути частиною коду.
Quolonel questions

10
як щодо кожного окремого файлу в каталозі рекурсивно?
ahnbizcad

4
Кожен раз, коли я намагаюся використати це, воно блокує деякі (як правило, всі) файли. : \
ThorSummoner

5
@ThorSummoner: якщо inputтой самий файл, що outputі bash, клобує вміст перед тим, як його навіть запустити expand. Так >працює.
Роберт Сімер

34

Збираючи кращі коментарі від відповіді Гена , найкраще рішення на сьогоднішній день, є використання spongeз moreutils .

sudo apt-get install moreutils
# The complete one-liner:
find ./ -iname '*.java' -type f -exec bash -c 'expand -t 4 "$0" | sponge "$0"' {} \;

Пояснення:

  • ./ рекурсивно шукає з поточного каталогу
  • -inameзбіг чутливо до регістру (для обох *.javaі *.JAVAподібних)
  • type -f знаходить лише звичайні файли (відсутні каталоги, бінарні файли чи посилання)
  • -exec bash -c виконати наступні команди в підкашлі для кожного імені файлу, {}
  • expand -t 4 розширює всі ТАБ на 4 місця
  • spongeзамочити стандартний вхід (від expand) і записати у файл (той самий) *.

ПРИМІТКА : * Просте перенаправлення файлу ( > "$0") не працюватиме тут, оскільки воно перезаписать файл занадто рано .

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


2
TIL: чудова команда губки після 15 років використання Linux. Дякую таємничому лицарю з Інтернету.
sscarduzio

19

Скористайтеся відхиленою косою рисою sed.

У Linux:

  • Замініть усі вкладки на 1 дефіс, у всіх * .txt файлах:

    sed -i $'s/\t/-/g' *.txt
  • Замініть всі вкладки на 1 місце у всіх файлах * .txt:

    sed -i $'s/\t/ /g' *.txt
  • Замініть всі вкладки 4 місцями у всіх * .txt файлах:

    sed -i $'s/\t/    /g' *.txt

На mac:

  • Замініть всі вкладки 4 місцями у всіх * .txt файлах:

    sed -i '' $'s/\t/    /g' *.txt

2
@ Машаsed -i '' $'s/\t/ /g' $(find . -name "*.txt")
xyzale

Ця відповідь здається найпростішою.
Ян Кінг Інь

6

Ви можете використовувати загальнодоступну prкоманду ( тут розміщена сторінка man ). Наприклад, щоб перетворити вкладки на чотири пробіли, зробіть це:

pr -t -e=4 file > file.expanded
  • -t придушує заголовки
  • -e=numрозширює вкладки на numпробіли

Щоб конвертувати всі файли в дереві каталогів рекурсивно, пропускаючи бінарні файли:

#!/bin/bash
num=4
shopt -s globstar nullglob
for f in **/*; do
  [[ -f "$f" ]]   || continue # skip if not a regular file
  ! grep -qI "$f" && continue # skip binary files
  pr -t -e=$num "$f" > "$f.expanded.$$" && mv "$f.expanded.$$" "$f"
done

Логіка пропускання бінарних файлів - з цієї публікації .

ПРИМІТКА:

  1. Це може бути небезпечним в git або svn repo
  2. Це неправильне рішення, якщо у вас є кодові файли, у яких вкладені вкладки в рядкові літерали

1
Будь-яка перевага перед expandтим, що обидва є POSIX? Наприклад, чи є варіант зміни вбудованої? Гіт безпеку на: stackoverflow.com/a/52136507/895245
Чіро Сантіллі郝海东冠状病六四事件法轮功

5

Як я можу конвертувати вкладки у пробіли у кожному файлі каталогу (можливо, рекурсивно)?

Зазвичай це не так те, що ви хочете.

Ви хочете зробити це для зображень PNG? PDF-файли? Каталог .git? Ваш Makefile(що вимагає вкладки)? Дамп SQL на 5 Гб?

Теоретично ви могли б передати цілий ряд варіантів виключення до того findчи іншого, що ви використовуєте; але це неміцно, і воно порушиться, як тільки ви додасте інші двійкові файли.

Що ви хочете, це принаймні:

  1. Пропускайте файли певного розміру.
  2. Визначте, чи файл є двійковим, перевіривши наявність байта NULL.
  3. Замініть вкладки лише на початку файлу ( expandце sed не так).

Наскільки я знаю, не існує «стандартної» утиліти Unix, яка б це могла зробити, і це не дуже просто зробити з одноланковою оболонкою оболонки, тому потрібен сценарій.

Нещодавно я створив маленький сценарій під назвою sanitize_files, який робить саме це. Він також виправляє деякі інші поширені речі, такі як заміна \r\nна \nдодавання останнього\n і т.д.

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

Я також хотів би зазначити у відповідь на деякі інші відповіді тут, що використання обшивки оболонки не є надійним способом цього зробити, тому що рано чи пізно у вас з’явиться більше файлів, ніж поміститься ARG_MAX(на сучасних системи Linux це 128к, який може здатися багато, але рано чи пізно це НЕ достатньо).


#!/usr/bin/env python
#
# http://code.arp242.net/sanitize_files
#

import os, re, sys


def is_binary(data):
    return data.find(b'\000') >= 0


def should_ignore(path):
    keep = [
        # VCS systems
        '.git/', '.hg/' '.svn/' 'CVS/',

        # These files have significant whitespace/tabs, and cannot be edited
        # safely
        # TODO: there are probably more of these files..
        'Makefile', 'BSDmakefile', 'GNUmakefile', 'Gemfile.lock'
    ]

    for k in keep:
        if '/%s' % k in path:
            return True
    return False


def run(files):
    indent_find = b'\t'
    indent_replace = b'    ' * indent_width

    for f in files:
        if should_ignore(f):
            print('Ignoring %s' % f)
            continue

        try:
            size = os.stat(f).st_size
        # Unresolvable symlink, just ignore those
        except FileNotFoundError as exc:
            print('%s is unresolvable, skipping (%s)' % (f, exc))
            continue

        if size == 0: continue
        if size > 1024 ** 2:
            print("Skipping `%s' because it's over 1MiB" % f)
            continue

        try:
            data = open(f, 'rb').read()
        except (OSError, PermissionError) as exc:
            print("Error: Unable to read `%s': %s" % (f, exc))
            continue

        if is_binary(data):
            print("Skipping `%s' because it looks binary" % f)
            continue

        data = data.split(b'\n')

        fixed_indent = False
        for i, line in enumerate(data):
            # Fix indentation
            repl_count = 0
            while line.startswith(indent_find):
                fixed_indent = True
                repl_count += 1
                line = line.replace(indent_find, b'', 1)

            if repl_count > 0:
                line = indent_replace * repl_count + line

        data = list(filter(lambda x: x is not None, data))

        try:
            open(f, 'wb').write(b'\n'.join(data))
        except (OSError, PermissionError) as exc:
            print("Error: Unable to write to `%s': %s" % (f, exc))


if __name__ == '__main__':
    allfiles = []
    for root, dirs, files in os.walk(os.getcwd()):
        for f in files:
            p = '%s/%s' % (root, f)
            if do_add:
                allfiles.append(p)

    run(allfiles)

У мерзотника, двоичная перевірка проста: stackoverflow.com/a/52136507/895245
Чіро Сантіллі郝海东冠状病六四事件法轮功

5

Мені подобається приклад "знайти" вище для рекурсивного застосування. Щоб адаптувати його до нерекурсивного, лише змінюючи файли в поточному каталозі, що відповідають підстановці, розширення глобальної оболонки може бути достатнім для невеликої кількості файлів:

ls *.java | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh -v

Якщо ви хочете мовчати після того, як ви впевнені , що це працює, просто перенесіть -vна shкоманду в кінці.

Звичайно, ви можете вибрати будь-який набір файлів у першій команді. Наприклад, перелічіть лише певний підкаталог (або каталоги) контрольованим чином, як це:

ls mod/*/*.php | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh

Або в свою чергу виконайте пошук (1) з деякою комбінацією параметрів глибини тощо:

find mod/ -name '*.php' -mindepth 1 -maxdepth 2 | awk '{print "expand -t 4 ", $0, " > /tmp/e; mv /tmp/e ", $0}' | sh

1
Раковина оболонки рано чи пізно зламається, оскільки загальна кількість імен файлів може бути лише ARG_MAXдовжиною. Це на 128 кб для систем Linux, але я досить часто стикався з цим обмеженням, щоб не покладатися на обшивку оболонок.
Мартін Турной

1
Вам не потрібно їх адаптувати. findможна сказати -maxdepth 1, і він обробляє лише записи зміненого каталогу, а не все дерево.
ShadowRanger

4

Я використовував astyleдля повторного відступу всього мого C / C ++ коду після пошуку змішаних вкладок та пробілів. У нього також є можливості примусити певний стиль брекету, якщо хочете.


4

Для цього можна використовувати vim:

find -type f \( -name '*.css' -o -name '*.html' -o -name '*.js' -o -name '*.php' \) -execdir vim -c retab -c wq {} \;

Як заявив Carpetsmoker, він буде відрегульований відповідно до ваших vimналаштувань. І моделей у файлах, якщо такі є. Також він замінить вкладки не тільки на початку рядків. Що не є тим, чого ти взагалі хочеш. Наприклад, у вас можуть бути літерали, що містять вкладки.


:retabзмінить усі вкладки у файлі, а не ті, які були на початку. це також залежить від того, які ваші :tabstopта :expandtabналаштування є у vimrc або modeline, тому це може не працювати взагалі.
Martin Tournoij

@Carpetsmoker Добрий момент про вкладки на початку рядків. Чи справляється з цим випадком будь-яке з рішень? Що стосується параметрів tabstopта expandtabналаштувань, то воно вийде, якщо ви користуєтесь vim. Якщо у файлах немає рядків режиму.
x-yuri

@ x-yuri гарне запитання, але загалом суперечить. Більшість людей використовують \ t не фактичні вкладки в літералах.
Рікардо Крус

4

Моя рекомендація:

find . -name '*.lua' -exec ex '+%s/\t/  /g' -cwq {} \;

Коментарі:

  1. Використовувати на місці редагування. Зберігайте резервні копії у VCS. Не потрібно створювати * .orig файли. Гарна практика відрізняти результат від останнього зобов'язання, щоб переконатися, що це спрацювало так, як очікувалося, у будь-якому випадку.
  2. sedє редактором потоку. Використовувати exдля редагування на місці Це дозволяє уникнути створення додаткових тимчасових файлів та нерестових оболонок для кожної заміни, як у верхній відповіді .
  3. ПОПЕРЕДЖЕННЯ. Це помилка з усіма вкладками, не лише тими, які використовуються для відступу. Крім того, це не робить контекстно усвідомленої заміни вкладок. Цього було достатньо для мого випадку використання. Але це може бути для вас неприйнятним.
  4. EDIT: Більш рання версія цієї відповіді використовується find|xargsзамість find -exec. Як вказував @ gniourf-gniourf, це призводить до проблем із пробілами, лапками та символами керування у назвах файлів пор. Уїлер .

exможе бути недоступним у кожній системі Unix. Заміна його vi -eможе працювати на інших машинах. Також ваш регулярний вимір замінює будь-яку кількість символів стартової вкладки двома пробілами. Замініть регулярний вираз, +%s/\t/ /gщоб не руйнувати багаторівневі відступи. Однак це також впливає на символи вкладок, які не використовуються для відступу.
Лукаш Шмельцейзен

ex є частиною POSIX [1], тому має бути доступною. Хороший момент щодо багаторівневої індендації. Я фактично використовував /\t/ /варіант у своїх файлах, але вирішив /\t\+//не порушувати вкладки без відступу. Пропущено проблеми з багатозаступництвом! Оновлення відповіді. [1] man7.org/linux/man-pages/man1/ex.1p.html#SEE%C2%A0ALSO
Генріх Хартманн

2
Використовувати xargsтаким чином марно, неефективно та ламано (подумайте про назви файлів, що містять пробіли чи лапки). Чому б вам не використовувати find«s -execперемикач замість цього?
gniourf_gniourf

Я б стверджував, що назви файлів з пробілами та лапки порушені; ) Якщо вам потрібна підтримка, яку я обрав би: -print0варіанти пошуку / xargs. Мені подобаються xargs з -execтих пір: а) Розмежування проблем b) її можна легше замінити на паралель GNU.
Генріх Хартманн

Оновлено додавання коментарів @gniourf_gniourf.
Генріх Хартманн

4

Щоб конвертувати всі файли Java рекурсивно в каталог, використовуйте 4 пробіли замість вкладки:

find . -type f -name *.java -exec bash -c 'expand -t 4 {} > /tmp/stuff;mv /tmp/stuff {}' \;

Чим ця відповідь відрізняється від тієї, яку було опубліковано 4 роки тому?
ПП

2
Так і ваша відповідь. Насправді це нижча версія відповіді Гена: 1) Відповідь Гена піклується про однойменні каталоги. 2) Він не рухається, якщо розширення не вдалося.
ПП

4

Ви можете використовувати findз tabs-to-spacesпакетом для цього.

Спочатку встановіть tabs-to-spaces

npm install -g tabs-to-spaces

потім запустіть цю команду з кореневого каталогу вашого проекту;

find . -name '*' -exec t2s --spaces 2 {} \;

Це замінить кожного tabсимволу на 2 spacesу кожному файлі.


3

Жодне тіло не згадане rpl? За допомогою rpl ви можете замінити будь-який рядок. Щоб перетворити вкладки в пробіли,

rpl -R -e "\t" "    "  .

дуже просто.


1
Це пошкодило всі бінарні файли мого репо.
Аарон Франке

1
Відмінна команда, але потенційно небезпечна за допомогою рекурсивного та всіх файлів у папці, як зазначено вище. Я б додав опцію --dry-run "про всяк випадок", щоб переконатися, що ви сидите в потрібній папці.
MortimerCat

2

Використання, expandяк пропонується в інших відповідях, видається найбільш логічним підходом лише для цього завдання.

Однак, це також можна зробити з Bash і Awk, якщо ви, можливо, захочете внести деякі інші модифікації разом з ним.

Якщо ви використовуєте Bash 4.0 або новішої версії, вбудований вбудований файл globstar можна використовувати для рекурсивного пошуку **.

З GNU Awk версії 4.1 або новішої версії можна змінити файл, подібний до "inplace":

shopt -s globstar
gawk -i inplace '{gsub("\t","    ")}1' **/*.ext

Якщо ви хочете встановити кількість пробілів на вкладці:

gawk -i inplace -v n=4 'BEGIN{for(i=1;i<=n;i++) c=c" "}{gsub("\t",c)}1' **/*.ext

2

Завантажте та запустіть наступний сценарій для рекурсивного перетворення жорстких вкладок у м'які вкладки у текстові файли.

Виконайте сценарій всередині папки, яка містить просто текстові файли.

#!/bin/bash

find . -type f -and -not -path './.git/*' -exec grep -Iq . {} \; -and -print | while read -r file; do {
    echo "Converting... "$file"";
    data=$(expand --initial -t 4 "$file");
    rm "$file";
    echo "$data" > "$file";
}; done;

2

Привітний метод сховища Git

git-tab-to-space() (
  d="$(mktemp -d)"
  git grep --cached -Il '' | grep -E "${1:-.}" | \
    xargs -I'{}' bash -c '\
    f="${1}/f" \
    && expand -t 4 "$0" > "$f" && \
    chmod --reference="$0" "$f" && \
    mv "$f" "$0"' \
    '{}' "$d" \
  ;
  rmdir "$d"
)

Дійте на всі файли в поточному каталозі:

git-tab-to-space

Дійте лише на файли C або C ++:

git-tab-to-space '\.(c|h)(|pp)$'

Ймовірно, ви хочете цього особливо через ті дратівливі Makefiles, для яких потрібні вкладки.

Команда git grep --cached -Il '':

  • перелічує лише відстежені файли, тому всередині нічого немає .git
  • виключає каталоги, двійкові файли (будуть пошкоджені) та символьні посилання (перетворюються на звичайні файли)

як пояснено в: Як перелічити всі текстові (небінарні) файли у сховищі git?

chmod --referenceзберігає дозволи файлу незмінними: /unix/20645/clone-ownership-and-permissions-from-another-file На жаль, я не можу знайти лаконічну альтернативу POSIX .

Якщо у вашій кодовій базі з'явилася шалена ідея дозволити функціональні вкладені необроблені вкладки, використовуйте:

expand -i

а потім отримуйте задоволення, переходячи всі вкладки рядків, що не починаються, одна за одною, яку ви можете перелічити: Чи можна git grep для вкладок?

Тестовано на Ubuntu 18.04.


-1

Перетворення вкладок у простір лише у файлах ".lua" [вкладки -> 2 пробіли]

find . -iname "*.lua" -exec sed -i "s#\t#  #g" '{}' \;

Очевидно, кількість місця, на яке розширюється вкладка, залежить від контексту. Таким чином, sed - це абсолютно невідповідний інструмент для виконання завдання.
Свен

?? @Sven, моя команда sed робить те саме, що робить команда розширення ( expand -t 4 input >output)
Makah,

3
Звичайно, ні. expand -t 4розширить вкладку a\tbна 3 місця, а вкладку - aa\tbна 2 проміжки, як і належить. expandвраховує контекст вкладки, sedне робить і замінить вкладку кількістю вказаних вами пробілів, незалежно від контексту.
Свен

-1

Використовуйте vim-шлях:

$ ex +'bufdo retab' -cxa **/*.*
  • Зробіть резервну копію! перед виконанням вищевказаної команди, оскільки це може пошкодити ваші бінарні файли.
  • Щоб використовувати globstar( **) для рекурсії, активуйте shopt -s globstar.
  • Щоб вказати конкретний тип файлу, використовуйте, наприклад: **/*.c.

Щоб змінити табло, додайте +'set ts=2'.

Однак нижня сторона полягає в тому, що вона може замінити вкладки всередині рядків .

Тож для дещо кращого рішення (за допомогою заміни) спробуйте:

$ ex -s +'bufdo %s/^\t\+/  /ge' -cxa **/*.*

Або за допомогою утиліти exредактор + expand:

$ ex -s +'bufdo!%!expand -t2' -cxa **/*.*

Додаткові пробіли див. У розділі: Як видалити пробіли пробілів для кількох файлів?


Ви можете додати в свою функцію наступну функцію .bash_profile:

# Convert tabs to spaces.
# Usage: retab *.*
# See: https://stackoverflow.com/q/11094383/55075
retab() {
  ex +'set ts=2' +'bufdo retab' -cxa $*
}

Я відповів багатьом відповідям у цій темі, а не лише вашій ;-) Причини: :retab можливо, взагалі не працює , обшивка оболонки - це погане рішення для подібних речей , ваша :sкоманда замінить будь-яку кількість вкладок на 2 пробіли (що ви майже ніколи не хочу), починати колишнього просто запустити :!expandпроцес - нерозумно ...
Мартін Турной

... і всі ваші рішення розкриють бінарні файли та подібні (наприклад .png файли, .pdf файли тощо)
Martin Tournoij

Це, відверто, жахлива пропозиція щодо документації - треба глибоко ознайомитися з низкою досить непрозорих синтаксисів та семантичних питань кількох програм, щоб мати змогу зрозуміти це.
Йосип Родін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.