我正在用bash编写一个简单的递归ls程序(我很没有经历过,所以请随意自大)。
该程序应该在一个单独的行上打印出每个文件(可能是目录),每次输入一个新目录时,输出都会移过4个空格,以给它一个树状的输出。
目前,它没有正确地打印出带空格的文件,并且它不会在目录之后放置正斜杠。 (详情如下。)
recls () {
# store current working directory
# issues: seems bad to have cwd defined up here and used down below in getAbsolutePath -- too much coupling
cwd=$PWD
# get absolute path of arg
argdir=`getAbsolutePath "$@"`
# check if it exists
if [ ! -e $argdir ]; then
echo "$argdir does not exist"
return 1
fi
echo "$argdir exists"
# check if it's a directory
if [ ! -d $argdir ]; then
echo "$argdir is not a directory"
return 2
fi
echo "$argdir is a directory"
tab=""
recls_internal $argdir
return 0
}
recls_internal () {
for file in $@; do
echo -n "$tab${file##/*/}"
if [ -d $file ]; then
# print forward slash to show it's a directory
echo "/"
savedtab=$tab
tab="$tab "
myls_internal $file/*
tab=$savedtab
else
# if not a directory, print a new line
echo ""
fi
done
}
getAbsolutePath () {
if [ -z ${1##/*} ]; then
echo "$1"
else
echo "$cwd/$1"
fi
}
该脚本包含在名为bash-practice
的文件夹中。当我recls .
时,我得到以下输出:
./
myls.sh
myls.sh~
recdir.sh
recls.sh
recls.sh~
sample
document.txt
sample-folder
sample-stuff
test-12.txt
test-1.txt
test-2.txt
sort-test.txt
sort-text-copy.txt
test-5-19-14-1
正如您所看到的,缩进工作正常但有两个问题:
1)文件sample document.txt
分布在两行,因为它有一个空格。
2)每个目录前面都应该有一个正斜杠,但出于某种原因只能在第一个目录上运行。
为了修复(1),我尝试保存内部文件分隔符并将其替换为换行符,如下所示:
...
tab=""
savedIFS=$IFS
IFS="\n"
recls_internal $argdir
IFS=$savedIFS
return 0
但这根本不起作用。它甚至没有显示超过第一个文件夹。显然,我对事情的理解是不正确的。
对于(2),我没有看到为什么它不能按预期工作的任何理由。
我去了mklement0建议的网站http://www.shellcheck.com,它的提示基本上都是双引号。当我双引"$@"
时,程序正确打印了文件sample document.txt
,但之后直接打印了{" binary operator expected
"错误。这是现在的样子打印出来的:
好吧,事实证明我有一个拼写错误导致它在递归时默认为我的函数的早期版本myls_internal
。此早期版本没有使用正斜杠标记目录。 "更新"中的错误消息部分也是固定的。我改变了行
myls_internal "$file/*"
到
recls_internal $file/*
现在它似乎正常工作。如果有人正在写答案,我仍然很感激你的见解,因为我并不真正理解引用的机制" $ @"修正了间距问题。
固定代码:
recls () {
# store current working directory
# issues: seems bad to have cwd defined up here and used down below in getAbsolutePath -- too much coupling
cwd=$PWD
# get absolute path of arg
argdir=$(getAbsolutePath "$@")
# check if it exists
if [ ! -e $argdir ]; then
echo "$argdir does not exist"
return 1
fi
echo "$argdir exists"
# check if it's a directory
if [ ! -d $argdir ]; then
echo "$argdir is not a directory"
return 2
fi
echo "$argdir is a directory"
tab=""
recls_internal $argdir
return 0
}
recls_internal () {
for file in "$@"; do
echo -n "$tab${file##/*/}"
if [ -d "$file" ]; then
# print forward slash to show it's a directory
echo "/"
savedtab=$tab
tab="$tab "
recls_internal $file/*
tab=$savedtab
else
# if not a directory, print a new line
echo ""
fi
done
}
getAbsolutePath () {
if [ -z ${1##/*} ]; then
echo "$1"
else
echo "$cwd/$1"
fi
}
固定输出:
该行
recls_internal $file/*
应该是
recls_internal "$file"/*
正确处理其中包含空格的目录。否则,包含cs 350
和Homework1.pdf
的{{1}}文件夹将扩展为
Homework2.pdf
什么时候应该
cs 350/Homework1.pdf 350/Homework2.pdf
我想?我并没有真正了解所发生的事情的细节,但这似乎解决了这个问题。
答案 0 :(得分:1)
为了说明"$@"
和$@
之间的区别,让我们考虑以下两个函数:
f() { for i in $@; do echo $i; done; }
g() { for i in "$@"; do echo $i; done; }
使用参数a "b c" "d e"
调用这些函数时,结果将是
f a "b c" "d e"
a
b
c
d
e
g a "b c" "d e"
a
b c
d e
因此,当“$ @”在双引号内时,扩展会将每个参数保存在单独的单词中(即使参数包含一个或多个空格)。 当扩展$ @(没有双引号)时,带空格的参数将被视为两个单词。
在您的脚本中,您还需要用双引号括住argdir
和file
。当目录或文件的名称包含空格时,它很有用,因此名称将被视为单个值。修改脚本下方。
#! /bin/bash -u
recls () {
# store current working directory
# issues: seems bad to have cwd defined up here and used down below in getAbsolutePath -- too much coupling
cwd=$PWD
# get absolute path of arg
argdir=`getAbsolutePath "$@"`
# check if it exists
if [ ! -e "$argdir" ]; then
echo "$argdir does not exist"
return 1
fi
echo "$argdir exists"
# check if it's a directory
if [ ! -d "$argdir" ]; then
echo "$argdir is not a directory"
return 2
fi
echo "$argdir is a directory"
tab=""
recls_internal "$argdir"
return 0
}
recls_internal () {
for file in "$@"; do
echo -n "$tab${file##/*/}"
if [ -d "$file" ]; then
# print forward slash to show it's a directory
echo "/"
savedtab=$tab
tab="$tab "
recls_internal "$file"/*
tab=$savedtab
else
# if not a directory, print a new line
echo ""
fi
done
}
getAbsolutePath () {
if [ -z ${1##/*} ]; then
echo "$1"
else
echo "$cwd/$1"
fi
}