我有一个文件,其中列出了文件/文件夹的完整路径。我正在寻找一种方法来像文件系统一样运行它。
我附加了此文件的一小部分(原始文件为 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
该答案将允许您执行以下操作:
根据清单,在您选择的目录中创建目录和常规文件的层次结构。这本身就是一个解决方案,因为您将能够使用
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
/path/to/directory
可能存在也可能不存在。如果需要,将会创建它。可以指定非空目录,但您可能希望使用空目录或尚不存在的目录。
笔记:
/path/to/directory
或许/dev/shm/something
为了提高性能。列表处理两次。首先创建目录(从以 结尾的行开始),然后创建
/
常规文件(从不以 结尾的行开始)。如果列表中常规文件的某些条目位于其目录条目之前,此方法仍可行。/
我假设对于不以 结尾的每一行,
/
都存在以 结尾的行,/
该行描述了路径中的所有目录。换句话说,当touch
尝试创建常规文件(例如./foo/bar/baz
)时,它所需的所有目录都已存在,因为mkdir
已创建它们(./foo/bar/
在列表中)。如果情况并非如此(即可能
./foo/bar/
缺少 ),请grep '…'
在行中将替换mkdir
为sed -n '\|^\./| s|/[^/]*$||p'
。此变体将使用所有条目来创建必要的目录(例如,./foo/bar/baz
仅创建 就足够了./foo/bar/
),因此touch
启动时,所有目录肯定都会存在。这将以性能为代价。您的列表使用换行符作为分隔符,但我不允许它们进入
xargs
。tr
我将它们转换为空字符,然后使用xargs -0
。xargs -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
笔记:
- 以root 身份
mount
使用后卸载;以用户身份使用后卸载。umount /path/to/mountpoint
squashfuse
fusermount -u /path/to/mountpoint
squashfuse
用途保险丝. 请参阅这些:- 由于压缩,图片很可能比列表小。如果您只想方便浏览,那么您可以删除列表并只使用图片。
附录
这是我用来创建类似于您的列表的列表的命令:
( 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
}