Shell可以识别~目录下的文件,但无法识别~/Documents目录下的文件。

5

我正在学习Unix,以下是作业中的一部分:

对于用户~/Documents目录中的每个文件和子目录,确定该项是文件还是目录,并使用文件名在语句中显示相应的信息。

因此,我写了以下内容:

docs=`ls ~/Documents`

for file in $docs ; do
    if [ -f $file ] ; then
        echo $file "is a file."
    elif [ -d $file ] ; then
        echo $file "is a directory."
    else
        echo $file "is not a file or directory."
    fi
done

我的文档目录包括以下文件和目录:

DocList.txt  (file)
Letter       (file)
mypasswdfile (file)
samples      (directory)
things       (directory)
touchfile    (file)

所以我认为输出应该是这样的:
DocList.txt is a file.
Letter is a file.
mypasswdfile is a file.
samples is a directory.
things is a directory.
touchfile is a file.

然而,这是输出结果:
DocList.txt is not a file or directory.
Letter is not a file or directory
mypasswdfile is not a file or directory
samples is not a file or directory
things is not a file or directory
touchfile is not a file or directory

我觉得我应该提一下,如果我把$docs变量设置为`ls ~',它将成功显示我的主目录的内容以及文件或目录的信息。但是对于我尝试过的其他路径,这种方法不起作用。


你是否已经查看过ls ~/Documents的输出了? - Ignacio Vazquez-Abrams
我的用户名是“alex”,没有空格。ls 〜/Documents 的输出以普通文件的白色文本和目录的蓝色文本列出其内容。我刚刚尝试了〜/ Documents /,不幸的是,输出仍然相同。 - Alex Tockey
1
提示:你正在从哪个目录运行这个脚本? - Roger Lipscombe
如果您的当前目录是~,当您键入ls DocList.txt时,会看到什么?将其与ls ~/Documents/DocList.txt进行比较。现在,考虑您的脚本正在执行相同的操作。 - glenn jackman
1
没错。这是parsing ls的缺点之一。你应该像这样做:for file in ~/Documents/*; do ... - glenn jackman
显示剩余5条评论
2个回答

2
问题出在你的ls命令上 - 你把ls的输出结果当作绝对路径,例如/home/alex/Documents/DocList.txt,但是当你执行ls ~/Documents命令时,它会打印出相对路径/文件名DocList.txt
为了得到预期的绝对路径行为,你可以使用find命令代替:

docs=`find ~/Documents`

如评论和另一个答案中提到的那样,为了能够处理文件名中的空格,您需要做以下操作:

docs=( ~/Documents/* )
for f in "${docs[@]}"; do
    ...

3
不,使用 bash 数组代替:docs=( ~/Documents/* ); for file in "${docs[@]}"; do ... -- 其他任何方法都无法处理文件名中带空格的情况。 - glenn jackman
我不理解你的评论。如果在~/Documents/中有一个名为hello world.txt的文件,for循环会如何运行? - glenn jackman
这种方式运行得很好,不过编辑:你是对的@glennjackman,我添加了一个带空格的文件,它将其解释为2个文件,并且无法确定它们是文件还是目录。 - Alex Tockey
@glennjackman - glglgl说OP的文件中没有任何空格。你也是正确的,处理空白更加健壮。 - cronburg
@glglgl,什么?Glenn是完全正确的:标量变量可以包含除NUL之外的任何字符;UNIX路径可以包含除NUL之外的任何字符;因此,如果您想处理所有边角情况(带有空格的文件名,带有换行符的文件名等等),则每个标量变量只能存储一个UNIX路径。因此,在这里使用数组而不是标量是正确的。 - Charles Duffy
1
@CharlesDuffy 是的,但是一个简单的 for file in ~/Documents/* 也可以工作。在我写评论时,我不知道 OP 使用了 ls - 这绝对是错误的。但是 OP 也错误地使用了变量,这是我想要评论的内容。 - glglgl

2
你遇到的问题是ls 命令只输出文件名而没有路径。
因此,你的$file变量只获取了文件名。
DocList.txt
Letter
mypasswdfile
samples
things
touchfile

从循环运行到循环运行。

如果您当前的目录不是~/Documents,测试这些文件名是错误的,因为这将在当前目录而不是预期的目录中搜索。

完成任务的一个更好的方法是

for file in ~/Documents/* ; do
    ...
done

这将设置$file为查找文件所需的每个完整路径名。

这样做后,它应该能够工作,但是非常容易出错:一旦您的路径或其中一个文件开始包含空格或其他空白字符,它就会失效。

对可能包含空格等内容的变量放置"非常重要。几乎没有理由不使用变量的"周围。

这里有什么区别?

使用[ -f $file ]file='something with spaces'[被调用的参数是-fsomethingwithspaces]。这肯定会导致错误行为。

另一方面,使用[ -f "$file" ]file='something with spaces'[被调用的参数是-fsomething with spaces]

因此,在shell编程中引用非常重要。

当然,对于[ -d "$file" ]也是如此。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接