我如何从文件中输入信息以将其作为文件系统逐步浏览

我如何从文件中输入信息以将其作为文件系统逐步浏览

我有一个文件,其中列出了文件/文件夹的完整路径。我正在寻找一种方法来像文件系统一样运行它。

我附加了此文件的一小部分(原始文件为 33MB,有超过 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
...

我想做的是如下事情

从根文件夹开始/

ls应该给出根目录

termux/
ubuntu-fs/
Other/
...

cd termux现在应该带我进入termux文件夹 ls,会列出termux文件夹中的所有内容

apt.tar
usr/
...

等等 ...

我不需要专门使用ls& cd。这只是为了解释我正在寻找的想法。

答案1

该答案将允许您执行以下操作:

  1. 根据清单,在您选择的目录中创建目录和常规文件的层次结构。这本身就是一个解决方案,因为您将能够使用cd+lsmc其他任何方式浏览层次结构。

  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

/path/to/directory可能存在也可能不存在。如果需要,将会创建它。可以指定非空目录,但您可能希望使用空目录或尚不存在的目录。

笔记:

  • /path/to/directory或许/dev/shm/something为了提高性能。

  • 列表处理两次。首先创建目录(从以 结尾的行开始),然后创建/常规文件(从不以 结尾的行开始)。如果列表中常规文件的某些条目位于其目录条目之前,此方法仍可行。/

  • 我假设对于不以 结尾的每一行,/都存在以 结尾的行,/该行描述了路径中的所有目录。换句话说,当touch尝试创建常规文件(例如./foo/bar/baz)时,它所需的所有目录都已存在,因为mkdir已创建它们(./foo/bar/在列表中)。

    如果情况并非如此(即可能./foo/bar/缺少 ),请grep '…'在行中将替换mkdirsed -n '\|^\./| s|/[^/]*$||p'。此变体将使用所有条目来创建必要的目录(例如,./foo/bar/baz仅创建 就足够了./foo/bar/),因此touch启动时,所有目录肯定都会存在。这将以性能为代价。

  • 您的列表使用换行符作为分隔符,但我不允许它们进入xargstr我将它们转换为空字符,然后使用xargs -0xargs -0这是逐字处理条目的最佳方式,无需特殊处理反斜杠和引号;但它不可移植。任何可移植的解决方案都有其怪癖和/或缺点,我不会详细说明。

  • grep(或)的正则表达式sed故意省略不以 开头的行./。这是因为某些原始路径名包含换行符并在列表中生成多行。示例:

    ./foo/bar/dir with
    newline character/baz
    

    上面的对中,只有第一行会被处理。错误的文件名将被截断,错误的目录将显示为常规文件;但至少顶级目录中不会出现误导性垃圾(newline character/baz如果创建,将是这样的垃圾)。

    由于我们最终使用xargs -0,更好的方法(但我不会实现它)是检测并处理此类情况,保留属于路径名的换行符并将其他换行符转换为空字符。但带有换行符-点-斜杠子字符串的路径名甚至会欺骗这一点。

    希望您创建列表的层次结构不包含带有换行符的路径名。

现在,您可以开始浏览层次结构。 中有一个可浏览的层次结构/path/to/directory。 如果不再需要,您可以使用 将其删除rm -r /path/to/directory。 保留脚本和列表并随时恢复层次结构;或者继续使用 SquashFS(继续阅读)。


2. 创建和使用 SquashFS 映像

使用上述脚本创建层次结构后,您现在可以创建 SquashFS 映像。如果层次结构为/path/to/directory,则创建映像的命令为:

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 中一样,让我能够测试我的脚本是否运行良好。

答案2

这是一个简单的 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
}

相关内容