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

У меня есть файл со списком файлов/папок с полными путями. Я ищу способ работать с этим, как если бы это была файловая система.

Я прикрепил небольшую часть этого файла (оригинал весит 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 ответа

Этот ответ позволит вам сделать следующее:

  1. Согласно листингу создайте иерархию каталогов и обычных файлов в выбранном вами каталоге. Это само по себе решение, поскольку вы сможете просматривать иерархию с помощьюcd+ls,mcили что-то еще.

  2. (Необязательно) Используя иерархию, создайте образ 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больше не нужен. Сохраняйте образ и монтируйте/размонтируйте его по своему желанию.

Примечания:


Приложение

Это команда, которую я использовал для создания объявления, похожего на ваше:

      ( 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
}
Другие вопросы по тегам