Как я могу ввести данные из файла, чтобы пройти через него как через файловую систему?
У меня есть файл со списком файлов/папок с полными путями. Я ищу способ работать с этим, как если бы это была файловая система.
Я прикрепил небольшую часть этого файла (оригинал весит 33 МБ и содержит более 460 000 строк).
./
./termux/
./termux/apt.tar
./termux/usr/
./termux/usr/lib/
./termux/usr/lib/libthread_db.so.1
./termux/usr/lib/libpython3.11.so.1.0
./termux/usr/lib/libgssapi_krb5.so.2.2
./termux/usr/lib/libTECkit.so
./termux/usr/lib/gnome-settings-daemon-3.0/
./termux/usr/lib/gnome-settings-daemon-3.0/gtk-modules/
./termux/usr/lib/gnome-settings-daemon-3.0/gtk-modules/at-spi2-atk.desktop
./termux/usr/lib/qt/
./termux/usr/lib/qt/mkspecs/
./termux/usr/lib/qt/mkspecs/integrity-armv7/
./termux/usr/lib/qt/mkspecs/integrity-armv7/qplatformdefs.h
./termux/usr/lib/qt/mkspecs/integrity-armv7/qmake.conf
./termux/usr/lib/qt/mkspecs/dummy/
./termux/usr/lib/gnome-settings-daemon-3.0/gtk-modules/canberra-gtk-module.desktop
...
./ubuntu-fs/
./ubuntu-fs/home/
...
./Other/
./Other/temp/
./Other/temp/Month.db
./Other/2021/
./Other/2021/January.txt
./Other/2021/February.txt
...
Я хотел бы сделать следующее:
начни с корневой папки/
должен дать список root
termux/
ubuntu-fs/
Other/
...
cd termux
должен взять меня в папку, теперь перечислит все, что есть вtermux
папка
apt.tar
usr/
...
И так далее ...
Мне не нужно работать конкретно сls
&cd
. Это просто для того, чтобы объяснить идею, которую я ищу.
2 ответа
Этот ответ позволит вам сделать следующее:
Согласно листингу создайте иерархию каталогов и обычных файлов в выбранном вами каталоге. Это само по себе решение, поскольку вы сможете просматривать иерархию с помощью
cd
+ls
,mc
или что-то еще.(Необязательно) Используя иерархию, создайте образ SquashFS для удобного монтирования (даже обычным пользователем) в будущем.
1. Создание иерархии
Возьмите этот скрипт:
#!/bin/sh -
file="$1"
dir="$2"
mkdir -p -- "$dir" || { echo >&2 'Cannot create directory.'; exit 1; }
<"$file" grep '^\./.*/$' | tr '\n' '\0' | (cd "$dir" && xargs -0 mkdir -p --)
<"$file" grep '^\./.*[^/]$' | tr '\n' '\0' | (cd "$dir" && xargs -0 touch --)
Сохраните это какtxt2dir
, сделайте его исполняемым (chmod +x txt2dir
) и используйте так:
./txt2dir /path/to/listing /path/to/directory
может существовать, а может и не существовать. Он будет создан при необходимости. Указание непустого каталога возможно, но, вероятно, вы захотите использовать пустой или еще не существующий каталог.
Примечания:
может быть
/dev/shm/something
для производительности.Листинг обрабатывается дважды. Каталоги (строки, заканчивающиеся на ) создаются первыми, обычные файлы (строки, не заканчивающиеся на ) создаются позже. Если в листинге какая-то запись для обычного файла предшествует записи для его каталога, это все равно будет работать.
Я предполагаю, что для каждой строки, не заканчивающейся на, существует строка, заканчивающаяся на
/
который описывает все каталоги на пути. Другими словами, когда он пытается создать обычный файл (например, ), все необходимые ему каталоги уже существуют, поскольку он их создал (был в листинге).Если это не обязательно так (т.е. если может отсутствовать), замените
grep '…'
в соответствии сmkdir
сsed -n '\|^\./| s|/[^/]*$||p'
. В этом варианте все записи будут использоваться для создания необходимых каталогов (например,./foo/bar/baz
одного будет достаточно, чтобы создать./foo/bar/
), так когдаtouch
запустится, все каталоги точно будут там. Это будет за счет производительности.В вашем списке в качестве разделителей используются символы новой строки, но я не разрешаю им вводить
xargs
. Сtr
Я конвертирую их в нулевые символы, а затем использую . это лучший способ обрабатывать записи дословно, без специальной обработки обратных косых черт и кавычек; хотя он не портативный. Любое портативное решение имеет свои особенности и/или недостатки, я не буду вдаваться в подробности.Регулярные выражения для
grep
(илиsed
) намеренно опускаем строки, не начинающиеся с./
. Это происходит в том случае, если какое-то исходное имя пути содержало символ новой строки и создавало более одной строки в списке. Пример:./foo/bar/dir with newline character/baz
Из приведенной выше пары будет обработана только первая строка. Неудачные имена файлов будут обрезаны, неудачные каталоги будут отображаться как обычные файлы; но, по крайней мере, в каталоге верхнего уровня не будет вводящего в заблуждение мусора (
newline character/baz
, если бы создали, была бы такая фигня).Поскольку в конечном итоге мы используем
xargs -0
, лучшим подходом (но я не собираюсь его реализовывать) было бы обнаруживать такие случаи и обрабатывать их, сохраняя символы новой строки, принадлежащие путям, и преобразуя другие символы в нулевые символы. Тем не менее, имена путей с подстрокой новой строки-точки-косой черты могли бы обмануть даже это.Надеемся, что иерархия, из которой вы создали свой список, не содержит путей с символами новой строки.
И вот. Доступная для просмотра иерархия находится в формате . Когда он больше не нужен, вы можете удалить его с помощьюrm -r /path/to/directory
. Сохраните сценарий и листинг и восстановите иерархию в любое время; или перейдите к SquashFS (продолжайте читать).
2. Создание и использование образа SquashFS.
Имея иерархию, созданную с помощью приведенного выше сценария, теперь вы можете создать образ SquashFS. Если иерархия находится в , команда для создания изображения:
mksquashfs /path/to/directory image.sqfs
Теперь вы можете смонтировать файловую систему:
как root с
mount -t squashfs image.sqfs /path/to/mountpoint
или как обычный пользователь с
squashfuse image.sqfs /path/to/mountpoint
(для этого необходимо, чтобы поддерживался и был включен в вашей ОС).
И теперь вы можете просматривать/path/to/mountpoint
. Иерархия в/path/to/directory
больше не нужен. Сохраняйте образ и монтируйте/размонтируйте его по своему желанию.
Примечания:
- Чтобы размонтировать после
mount
использоватьumount /path/to/mountpoint
как корень; отмонтировать после использованияfusermount -u /path/to/mountpoint
как пользователь. -
squashfuse
использует FUSEПРЕДОХРАНИТЕЛЬ. Посмотрите это: - Из-за сжатия изображение, скорее всего, будет меньше листинга. Если вам нужен удобный просмотр, вы можете удалить список и работать только с изображением.
Приложение
Это команда, которую я использовал для создания объявления, похожего на ваше:
( cd /path/to/hierarchy && find . ! -type d -print -o -exec printf '%s/\n' {} \; ) > listing
Я также использовал его для создания списков из иерархий, созданных на основе исходного списка. Это иdiff <(sort listing) <(sort listing2)
в Bash позволил мне проверить, хорошо ли работает мой сценарий.
Вот доказательство концепции простой программы на Perl, которая делает то, что вам нужно (но я не рассмотрел все возможные крайние случаи). Дайте ему имя входного файла в качестве аргумента.
#!/usr/bin/perl
use warnings;
use strict;
use feature qw{ say };
sub dir :lvalue {
my ($fs, @dirs) = @_;
return '.' unless @dirs;
return $fs->{ $dirs[0] } if 1 == @dirs;
return dir($fs->{ $dirs[0] }, @dirs[1 .. $#dirs])
}
my %fs;
while (<>) {
chomp;
my $is_dir = m{/$};
my @dirs = split m{/};
my $last = pop @dirs;
if ($is_dir) {
dir(\%fs, @dirs, $last) = {};
} else {
dir(\%fs, @dirs)->{$last} = undef;
}
}
my $current = '.';
sub ls {
my @args = @_;
@args = ("") unless @args;
for my $arg (@args) {
if ($arg !~ m{^/}) {
substr $arg, 0, 0, "$current/";
}
my @dirs = updirs(split m{/}, $arg);
for my $member (sort keys %{ dir(\%fs, @dirs) }) {
print $member;
say dir(\%fs, @dirs)->{$member} ? '/' : "";
}
}
}
sub cd {
my @args = @_;
warn "One argument expected.\n" unless 1 == @args;
my @dirs = updirs(split m{/},
($args[0] =~ m{^/}) ? $args[0] : "$current/$args[0]");
if (ref dir(\%fs, @dirs)) {
$current = join '/', @dirs;
} else {
warn "Non-existent path.\n";
}
}
my %dispatch = (ls => \&ls,
cd => \&cd,
exit => sub { exit });
while (print({*STDERR} "$current: "), $_ = <STDIN>) {
chomp;
my ($command, @args) = split;
warn "Unknown command '$command'.\n"
unless exists $dispatch{$command};
$dispatch{$command}->(@args);
}
sub updirs {
my @dirs = @_;
shift @dirs if "" eq $dirs[0];
for (my $i = 0; $i <= $#dirs; ++$i) {
if ($dirs[$i] eq '..') {
if ($i > 0) {
splice @dirs, $i - 1, 2;
$i -= 2;
} else {
splice @dirs, 0, 1;
$i = -1;
}
}
}
return @dirs
}