tr: транслитерация и удаление символов
tr (translate or delete characters) — character-by-character трансформация. Не работает с строками, не с regex — только с символами. Принимает stdin, пишет на stdout.
$ echo "hello" | tr 'a-z' 'A-Z'
HELLO
$ echo "hello world" | tr ' ' '_'
hello_world
$ echo "hello123" | tr -d '0-9'
hello
$ echo "aaabbbccc" | tr -s 'a-z'
abc # -s = squeeze repeats
Каждая опция — простое посимвольное действие.
Канонические использования
# Заменить запятые на табы (CSV -> TSV)
$ tr ',' '\t' < orders.csv > orders.tsv
# Удалить carriage returns (Windows -> Unix line endings)
$ tr -d '\r' < windows_file.txt > unix_file.txt
# Слова на отдельных строках (для wc/sort)
$ echo "hello world foo bar" | tr ' ' '\n'
hello
world
foo
bar
# Lowercase
$ echo "HELLO" | tr 'A-Z' 'a-z'
hello
# Squeeze multiple spaces
$ echo "hello world !" | tr -s ' '
hello world !
# Только digits и буквы (удалить punctuation)
$ echo "hello, world!" | tr -cd '[:alnum:]\n'
helloworld
[:alnum:], [:digit:], [:space:] — POSIX character classes. Удобно с tr -c для sanitization.
Tr НЕ умеет
- Multi-character substitution (
tr 'foo' 'bar'не заменит ‘foo’ на ‘bar’ — это посимвольная заменаf->b, o->a, o->r). - Regex.
- Conditional transformation.
Для multi-char — используй sed.
paste: column merge
paste склеивает строки по горизонтали: берёт строку N из каждого файла и соединяет в одну строку через TAB.
$ cat names.txt
alice
bob
carol
$ cat ages.txt
30
25
28
$ paste names.txt ages.txt
alice 30
bob 25
carol 28
С -d разделитель:
$ paste -d ',' names.txt ages.txt
alice,30
bob,25
carol,28
С -s (serial) — собрать все строки одного файла в одну, разделяя через TAB или -d:
$ paste -s -d ',' names.txt
alice,bob,carol
DE-сценарии paste
# Создать CSV из отдельных колонок
$ paste -d ',' headers.txt names.txt ages.txt > data.csv
# Превратить список строк в comma-separated:
$ paste -s -d ',' tags.txt
sql,python,airflow,kafka
# Объединить два sorted-файла по строкам:
$ paste file1.sorted file2.sorted | awk -F'\t' '{print $1, $2}'
comm: set operations на отсортированных файлах
comm сравнивает два отсортированных файла построчно и выводит три колонки:
- строки только в file1
- строки только в file2
- строки в обоих (common)
$ cat file1.sorted
apple
banana
cherry
$ cat file2.sorted
banana
cherry
date
$ comm file1.sorted file2.sorted
apple
banana
cherry
date
(Здесь tab indent показывает, в какой колонке строка.)
Фильтр через флаги:
-1подавляет колонку 1-2подавляет колонку 2-3подавляет колонку 3
Канонические использования:
# Общие строки (intersection)
$ comm -12 file1.sorted file2.sorted
# Только в file1 (difference: A - B)
$ comm -23 file1.sorted file2.sorted
# Только в file2
$ comm -13 file1.sorted file2.sorted
# Уникальные строки одного из файлов (symmetric difference)
$ comm -3 file1.sorted file2.sorted
DE-сценарии comm
1. Новые users сегодня (которых не было вчера)
$ comm -13 <(sort users_yesterday.txt) <(sort users_today.txt)
new_alice
new_bob
<(...) — process substitution (про это в модуле 9). Запускает команду как файл — комфортно, когда сортировка нужна на лету.
2. Удалённые users (были вчера, нет сегодня)
$ comm -23 <(sort users_yesterday.txt) <(sort users_today.txt)
gone_carol
3. Common между двумя dataset
# Users, которые были и вчера и сегодня:
$ comm -12 <(sort users_yesterday.txt) <(sort users_today.txt)
4. Set difference — для data quality
# Проверка: все expected files есть в actual:
$ comm -23 <(sort expected.txt) <(sort actual.txt)
# Если вывод пуст — все expected присутствуют в actual
# Если есть строки — это missing files
SQL INTERSECT и EXCEPT — set-операции на уровне запросов
Comm требует sorted input
Без sort:
$ comm file1 file2
comm: file 1 is not in sorted order
Возможны два пути: pre-sort через sort или используй <(sort ...) inline. Или sort -u если хочешь скрытно убрать дубликаты.
# Inline sort:
$ comm -12 <(sort file1) <(sort file2)
# С удалением дубликатов:
$ comm -12 <(sort -u file1) <(sort -u file2)
diff: line-by-line сравнение
diff — про что-то другое: показывает построчные изменения между двумя файлами. Используется для патчей, code review, версионирования.
$ cat v1.txt
line1
line2
line3
$ cat v2.txt
line1
line2-modified
line4
$ diff v1.txt v2.txt
2c2
< line2
---
> line2-modified
3c3
< line3
---
> line4
Формат вывода:
2c2— строка 2 файла1 changed на строку 2 файла2<— строка из файла1>— строка из файла2
Полезные опции:
git diff — unified format и patch workflow$ diff -u v1.txt v2.txt # unified format (для git patches)
$ diff -y v1.txt v2.txt # side-by-side
$ diff -r dir1 dir2 # рекурсивно по директориям
$ diff -q dir1 dir2 # quiet — только сообщает 'files differ'
$ diff --brief # эквивалент -q
$ diff --color=auto # с цветами (GNU diff)
DE-применения diff
# Что изменилось в exported данных
$ diff snapshot_v1.csv snapshot_v2.csv | head -30
# Сравнение конфигов между серверами
$ ssh prod cat /etc/airflow.cfg | diff - /local/airflow.cfg
DE-pipeline: find new rows since last run
# Сегодняшний и вчерашний дамп users:
$ comm -13 <(sort yesterday.csv) <(sort today.csv) > new_users.csv
# Затем — обработка:
$ wc -l new_users.csv
142 new_users.csv
# Для упомянутого dataset из урока (orders.csv):
$ comm -13 <(sort yesterday_orders.csv) <(sort today_orders.csv) \
| tee new_orders.csv \
| awk -F',' '{sum+=$3} END {print "New orders sum:", sum}'
Это incremental loading pattern: каждый день обрабатывать только новые строки.
Попробуй сам
- Lower->upper:
echo "hello" | tr 'a-z' 'A-Z' - Удалить цифры:
echo "abc123def456" | tr -d '0-9' # -> abcdef - Sanitize input — только alphanumeric:
echo "hello, world! 123 @ #" | tr -cd '[:alnum:][:space:]' - Two-file diff:
printf "a\nb\nc\n" > /tmp/f1 printf "a\nB\nc\n" > /tmp/f2 diff /tmp/f1 /tmp/f2 - Set intersection:
printf "apple\nbanana\ncherry\n" | sort > /tmp/fruits1 printf "banana\ncherry\ndate\n" | sort > /tmp/fruits2 comm -12 /tmp/fruits1 /tmp/fruits2 # -> banana, cherry
macOS-различия
tr: BSD tr на macOS имеет небольшие различия — некоторые ranges с UTF-8 работают непредсказуемо. Для гарантии используйLC_ALL=C tr ....paste: идентичен.comm: идентичен. Требует sort с тем же locale (использует strcmp).diff: BSD diff не поддерживает--color=auto— используй GNU diff (brew install diffutils->gdiff).
Главное
tr SET1 SET2— посимвольная замена.tr -d SETудалить.tr -s SETsqueeze.tr -c SETcomplement.paste file1 file2— column merge (TAB separator).paste -d ','для CSV.paste -sсклеить в одну строку.comm file1 file2(отсортированных) — три колонки: only1, only2, both. Флаги-1/-2/-3подавляют колонки.- Каноны:
comm -12(intersection),comm -23(only in file1),comm -13(only in file2). diff file1 file2— построчное сравнение.-uunified format,-rrecursive,-qquiet.commдля set-операций на datasets (incremental loading).diffдля patches и code review.- Process substitution
<(sort file)— sort на лету без temp-файла.