- 前言
- 最终解决方案
- 代码
- 看起来像这样
- 使用方式
- 获取用户选择的文件/文件夹
- 我怎么知道用户选择的是文件还是文件夹
- 我可不可以在C等语言调用这个脚本啊
有时候部署环境,需要配置文件路径或者载入相应的配置文件。这种情况下,如果用传统的手动修改配置的方式配置,会比较容易出意外(比如中文符号和英文符号混淆、大小写错误、l和i混淆等)。
这个时候,我解决的方式就是模仿Windows下常见的文件对话框,在下载文件、打开文件时Windows常见的文件对话框。与Windows不同的是,我要在命令行界面下使用图形化的对话框。写库是不可能写库的,我着急着用这个功能,所以就直接用了现成的dialog工具来完成。
因为我是在部署环境的场景下使用,所以就不用C或C++写这个代码了,使用bash代码。依赖dialog,可以使用sudo yum install dialog或sudo 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,所以是文件夹。
可以,调用的方法很多,如:
#include#include 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, "你选择了: %sn", 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#include #include char bash_script_data[4096]; char *browse_dir(const char *init_dir, const char *output) { strcpy(bash_script_data, "#!/bin/bashn" "ERROR_CD=1n" "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"; thenn" " return $ERROR_CDn" " fin" " local list=$(ls -aQ)n" " local old_IFS=$IFSn" " IFS=$'\n'n" " listarr=($list)n" " IFS=$old_IFSn" " list=''n" " for i in ${!listarr[*]}; don" " list="$list ${listarr[$i]} \"$(eval "date -d @\$(stat ${listarr[$i]} -c %Y) '+%Y-%m-%d %T'") $(eval "stat ${listarr[$i]} -c %F")\""n" " donen" " eval "dialog --ok-label '$ENTER_LABEL' --cancel-label '$DONE_LABEL' --title '$TITLE' --menu '${TIP}${init_dir}' 0 0 0 $list 2>'$tmpfile'"n" " return 0n" "}n" "function next_action()n" "{n" " local item=$1n" " if [ "$item" == "" ]; thenn" " echo "$(pwd)" >"$tmpfile"n" " return 0n" " fin" " local typ=$(ls -ld "$item" | cut -c 1)n" " if [ "$typ" == "d" ]; thenn" " browse_dir "$item"n" " elif [ "$typ" == "l" ]; thenn" " local lnk=$(readlink "$item")n" " if ! cd "$(dirname "$lnk")"; thenn" " return $ERROR_CDn" " fin" " next_action "$(basename "$lnk")"n" " elsen" " echo "$(realpath "$item")" >"$tmpfile"n" " fin" "}n" "function browse_dir()n" "{n" " local statusn" " local init_dir=$1n" " show "$init_dir"n" " status=$?n" " if [ $status -ne 0 ]; thenn" " return $statusn" " fin" " 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 ]; thenn" " exit $statusn" "fin" "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, "你选择了: %sn", 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; }
输出:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)