Linux bash 命令行下实现可视化文件或文件夹浏览

本文介绍了如何在Linux命令行环境下使用bash脚本结合dialog工具实现一个图形化的文件或文件夹选择对话框。通过这个对话框,用户可以在终端中直观地浏览和选择文件或目录,避免手动输入带来的错误。脚本同时提供了在C等语言中调用的示例,方便集成到其他程序中。
摘要由CSDN通过智能技术生成
Linux bash 命令行下实现可视化文件或文件夹浏览

前言

  有时候部署环境,需要配置文件路径或者载入相应的配置文件。这种情况下,如果用传统的手动修改配置的方式配置,会比较容易出意外(比如中文符号和英文符号混淆、大小写错误、l和i混淆等)。
  这个时候,我解决的方式就是模仿Windows下常见的文件对话框,在下载文件、打开文件时Windows常见的文件对话框。与Windows不同的是,我要在命令行界面下使用图形化的对话框。写库是不可能写库的,我着急着用这个功能,所以就直接用了现成的dialog工具来完成。

最终解决方案

代码

  因为我是在部署环境的场景下使用,所以就不用C或C++写这个代码了,使用bash代码。依赖dialog,可以使用sudo yum install dialogsudo apt-get install dialog安装dialog。

#!/bin/bash

ERROR_CD=1		# 文件夹无法进入

ENTER_LABEL="选择文件/进入文件夹"
DONE_LABEL="选择当前文件夹"
TITLE="浏览"
TIP="当前路径: "

tmpfile=$(mktemp)

function show()
{
	local init_dir=$(realpath "$1")
	if ! cd "$init_dir"; then
		return $ERROR_CD
	fi
	local list=$(ls -aQ)	# -a 显示隐藏文件, -Q 每个文件左右两边添加双引号并转义特殊字符
	local old_IFS=$IFS
	IFS=$'\n'
	listarr=($list)	# 按换行符分割
	IFS=$old_IFS
	list=''
	for i in ${!listarr[*]}; do
		# 每一个文件, 添加修改日期和文件类型信息到右边
		list="$list ${listarr[$i]} \"$(eval "date -d @\$(stat ${listarr[$i]} -c %Y) '+%Y-%m-%d %T'")    $(eval "stat ${listarr[$i]} -c %F")\""
	done
	eval "dialog --ok-label '$ENTER_LABEL' --cancel-label '$DONE_LABEL' --title '$TITLE' --menu '${TIP}${init_dir}' 0 0 0 $list 2>'$tmpfile'"
	return 0
}

function next_action()
{
	local item=$1
	if [ "$item" == "" ]; then
		# 选择了当前文件夹, 直接输出当前路径到缓存文件
		echo "$(pwd)" >"$tmpfile"
		return 0
	fi
	local typ=$(ls -ld "$item" | cut -c 1)
	if [ "$typ" == "d" ]; then
		# 是目录, 进入
		browse_dir "$item"
	elif [ "$typ" == "l" ]; then
		# 是符号链接, 判断链接到的文件是目录还是其他
		local lnk=$(readlink "$item")
		if ! cd "$(dirname "$lnk")"; then
			return $ERROR_CD
		fi
		next_action "$(basename "$lnk")"
	else
		# 其他当普通文件处理, 文件名输出到缓存文件
		echo "$(realpath "$item")" >"$tmpfile"
	fi
}

function browse_dir()
{
	local status
	local init_dir=$1
	show "$init_dir"
	status=$?
	if [ $status -ne 0 ]; then
		return $status
	fi
	local item=$(cat "$tmpfile")
	next_action "$item"
}

INIT_DIR=${1:-./}

browse_dir "$INIT_DIR"
status=$?
if [ $status -ne 0 ]; then
	exit $status
fi
result=$(cat "$tmpfile")
rm -f "$tmpfile"
echo "$result" >&2
exit 0

看起来像这样

在这里插入图片描述

注:不同终端工具下显示的效果是不一样的。

使用方式

  假设上述代码保存到browse_dir.sh,并且已经添加可执行权限。

./browse_dir.sh

  如上即可显示一个文件对话框,初始路径为当前路径。
  如果需要设置初始路径,可以在参数指定,如:

./browse_dir.sh /home	# 初始路径设置为 /home

获取用户选择的文件/文件夹

  我把用户选择放到了标准错误输出里了,也就是通过重定向标准错误输出到缓存文件,然后读取这个缓存文件就可以获取到用户的选择了。例如:

tmpfile=$(mktemp)
./browse_dir.sh / 2>$tmpfile
cat $tmpfile
rm -f $tmpfile

  输出:
在这里插入图片描述
在这里插入图片描述

我怎么知道用户选择的是文件还是文件夹

  要判断用户选择的是文件还是文件夹,需要自行通过命令判断,如:

ls -ld <用户选择的结果> | cut -c 1

  如果输出d表示用户选择的是文件夹,输出-表示用户选择的是文件。如:
在这里插入图片描述
  如上输出了-,所以表示是文件。
在这里插入图片描述
  如上输出了d,所以是文件夹。

我可不可以在C等语言调用这个脚本啊

  可以,调用的方法很多,如:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	char tmp[11] = "tmp.XXXXXX";
	char cmd[512] = {0};
	char item[261] = {0};
	char typ;
	FILE *tmpfile = NULL;
	int tmpfd = mkstemp(tmp);
	close(tmpfd);
	sprintf(cmd, "./browse_dir.sh / 2>%s", tmp);
	system(cmd);
	tmpfile = fopen(tmp, "rb");
	fread(item, 260, 1, tmpfile);
	fclose(tmpfile);

	strtok(item, "\n");
	fprintf(stdout, "你选择了: %s\n", item);
	fprintf(stdout, "它是一个: ");

	sprintf(cmd, "ls -ld %s | cut -c 1 >%s", item, tmp);
	system(cmd);
	tmpfile = fopen(tmp, "rb");
	fread(&typ, 1, 1, tmpfile);
	fclose(tmpfile);	

	if (typ == 'd')
	{
		fprintf(stdout, "文件夹\n");
	}
	else
	{
		fprintf(stdout, "文件\n");
	}

	remove(tmp);
	return 0;
}

  如果想把脚本直接放到C代码里也可以这样写:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char bash_script_data[4096];
char *browse_dir(const char *init_dir, const char *output)
{
	strcpy(bash_script_data, "#!/bin/bash\n"
	"ERROR_CD=1\n"
	"ENTER_LABEL=\"选择文件/进入文件夹\"\n"
	"DONE_LABEL=\"选择当前文件夹\"\n"
	"TITLE=\"浏览\"\n"
	"TIP=\"当前路径: \"\n"
	"savedir=$(pwd)\n"
	"tmpfile=$(mktemp)\n"
	"function show()\n"
	"{\n"
	"	local init_dir=$(realpath \"$1\")\n"
	"	if ! cd \"$init_dir\"; then\n"
	"		return $ERROR_CD\n"
	"	fi\n"
	"	local list=$(ls -aQ)\n"
	"	local old_IFS=$IFS\n"
	"	IFS=$'\\n'\n"
	"	listarr=($list)\n"
	"	IFS=$old_IFS\n"
	"	list=''\n"
	"	for i in ${!listarr[*]}; do\n"
	"		list=\"$list ${listarr[$i]} \\\"$(eval \"date -d @\\$(stat ${listarr[$i]} -c %Y) '+%Y-%m-%d %T'\")    $(eval \"stat ${listarr[$i]} -c %F\")\\\"\"\n"
	"	done\n"
	"	eval \"dialog --ok-label '$ENTER_LABEL' --cancel-label '$DONE_LABEL' --title '$TITLE' --menu '${TIP}${init_dir}' 0 0 0 $list 2>'$tmpfile'\"\n"
	"	return 0\n"
	"}\n"
	"function next_action()\n"
	"{\n"
	"	local item=$1\n"
	"	if [ \"$item\" == \"\" ]; then\n"
	"		echo \"$(pwd)\" >\"$tmpfile\"\n"
	"		return 0\n"
	"	fi\n"
	"	local typ=$(ls -ld \"$item\" | cut -c 1)\n"
	"	if [ \"$typ\" == \"d\" ]; then\n"
	"		browse_dir \"$item\"\n"
	"	elif [ \"$typ\" == \"l\" ]; then\n"
	"		local lnk=$(readlink \"$item\")\n"
	"		if ! cd \"$(dirname \"$lnk\")\"; then\n"
	"			return $ERROR_CD\n"
	"		fi\n"
	"		next_action \"$(basename \"$lnk\")\"\n"
	"	else\n"
	"		echo \"$(realpath \"$item\")\" >\"$tmpfile\"\n"
	"	fi\n"
	"}\n"
	"function browse_dir()\n"
	"{\n"
	"	local status\n"
	"	local init_dir=$1\n"
	"	show \"$init_dir\"\n"
	"	status=$?\n"
	"	if [ $status -ne 0 ]; then\n"
	"		return $status\n"
	"	fi\n"
	"	local item=$(cat \"$tmpfile\")\n"
	"	next_action \"$item\"\n"
	"}\n"
	"INIT_DIR=");
	strcat(bash_script_data, init_dir);
	strcat(bash_script_data, "\n"
	"browse_dir \"$INIT_DIR\"\n"
	"status=$?\n"
	"if [ $status -ne 0 ]; then\n"
	"	exit $status\n"
	"fi\n"
	"result=$(cat \"$tmpfile\")\n"
	"rm -f \"$tmpfile\"\n"
	"cd \"$savedir\"\n"
	"echo \"$result\" >");
	strcat(bash_script_data, output);
	strcat(bash_script_data, "\n");
	return bash_script_data;
}

int main()
{
	char tmp[11] = "tmp.XXXXXX";
	char cmd[512] = {0};
	char item[261] = {0};
	char typ;
	FILE *tmpfile = NULL;
	int tmpfd = mkstemp(tmp);
	close(tmpfd);

	system(browse_dir("/", tmp));
	tmpfile = fopen(tmp, "rb");
	fread(item, 260, 1, tmpfile);
	fclose(tmpfile);

	strtok(item, "\n");
	fprintf(stdout, "你选择了: %s\n", item);
	fprintf(stdout, "它是一个: ");

	sprintf(cmd, "ls -ld %s | cut -c 1 >%s", item, tmp);
	system(cmd);
	tmpfile = fopen(tmp, "rb");
	fread(&typ, 1, 1, tmpfile);
	fclose(tmpfile);	

	if (typ == 'd')
	{
		fprintf(stdout, "文件夹\n");
	}
	else
	{
		fprintf(stdout, "文件\n");
	}

	remove(tmp);
	return 0;
}

  输出:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值