如何在Shell脚本中获取目录中的文件列表?


159

我正在尝试使用Shell脚本获取目录的内容。

我的脚本是:

for entry in `ls $search_dir`; do
    echo $entry
done

$search_dir相对路径在哪里。但是,$search_dir包含许多名称中带有空格的文件。在这种情况下,该脚本不会按预期运行。

我知道我可以使用for entry in *,但这仅适用于当前目录。

我知道我可以切换到该目录,使用for entry in *然后再改回,但是我的特殊情况使我无法这样做。

我有两个相对路径$search_dir$work_dir,并且我必须同时处理这两个路径,读取它们并在其中创建/删除文件等。

那我现在该怎么办?

PS:我用bash。

Answers:


274
for entry in "$search_dir"/*
do
  echo "$entry"
done

4
您能解释为什么for entry in "$search_dir/*"不起作用吗?为什么我们需要放在/*引号之外?
mrgloom '16

6
@mrgloom:因为您需要让shell通配符通配。
伊格纳西奥·巴斯克斯

会不会find更快?
Alexej Magura

4
解决方案给出了完整的路径。如果我只想列出当前目录中的内容怎么办?
ThatsRightJack

1
@mrgloom如果您想这样做,可以使用for entry in "${search_dir}/*"
funilrys 17/08/12

28

此处的其他答案很好,可以回答您的问题,但这是google在“ bash获取目录中文件列表”方面的最佳搜索结果(我一直在寻找保存文件列表的方法),所以我想我应该发布一个这个问题的答案:

ls $search_path > filename.txt

如果只需要某种类型(例如任何.txt文件):

ls $search_path | grep *.txt > filename.txt

注意$ search_path是可选的;ls> filename.txt将执行当前目录。


无需使用grep即可仅获取.txt文件:“ ls $ search_path / *。txt> filename.txt”。但更重要的是,不应使用ls命令的输出来分析文件名。
Victor Zamanian

1
@VictorZamanian,您能详细说明为什么我们不应该使用输出ls解析文件名吗?以前没有听说过。
samurai_jane

1
@samurai_jane有很多与此主题相关的链接,但这是第一个搜索结果:mywiki.wooledge.org/ParsingLs。我什至在SO上看到一个问题,声称不解析ls输出的原因是BS,并且非常详细。但是答复/回答仍然声称这是一个坏主意。看看:unix.stackexchange.com/questions/128985/...
维克多Zamanian

26

这是一种使语法更易于理解的方式:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./是当前的工作目录,但可以用任何路径替换,
*.txt返回的内容是什么。txt
您可以通过ls直接在终端中键入命令来检查列出的内容。

基本上,您将创建一个yourfilenames包含list命令作为单独元素返回的所有内容的变量,然后在其中循环遍历。循环创建一个临时变量eachfile,该变量包含要循环通过的变量的单个元素,在本例中为文件名。这不一定比其他答案更好,但是我发现它很直观,因为我已经熟悉该ls命令和for循环语法。


对于快速,非正式的脚本或单行代码,此方法可以正常工作,但与基于glob的解决方案不同,如果文件名包含换行符,它将中断。
索伦·比约恩斯塔德

@SorenBjornstad感谢您的建议!我不知道文件名中允许使用换行符-什么样的文件可以包含它们?就像,这是经常发生的事情吗?
rrr

由于这个原因,文件名中的换行符是邪恶的,据我所知,没有合理的理由使用它们。我自己从未在野外见过。就是说,完全有可能以这种方式恶意地使用换行符构造文件名。(例如,假设一个包含文件AB和的目录C。您创建名为B\nC和的文件D,然后选择删除它们。不处理此权限的软件可能最终会删除先前存在的文件B和C,即使您没有允许这样做。)
Soren Bjornstad

19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done

11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

1
我知道这已经很老了,但是我似乎看不到最后一个xargs -0 -i echo "{}"命令,请稍稍解释一下吗?特别是这-i echo "{}"部分是做什么的?另外,我从现在不建议使用的man页面中读取内容-i,我们应该使用-Iinsted。
drkg4b

1
-i{}用arg 替代。
Noel Yap 2015年

谢谢!这也很有用,对于像我这样的头脑迟钝的人,我认为the {}是被find命令替换为匹配项的字符串。
drkg4b 2015年

3
你为什么用xargs?默认情况下,find打印找到的内容...您可以从中删除所有内容-print0
gniourf_gniourf 2015年

1
这样做不能很好地处理带有空格的文件条目。
Noel Yap

4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

变化

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

笔记:

  • . :当前文件夹
  • 删除-maxdepth 1以递归搜索
  • -type f:查找文件,而不是目录(d
  • -not -path '*/\.*' :不返回 .hidden_files
  • sed 's/^\.\///g'./从结果列表中删除前置的

0

这是在目录内列出文件的另一种方式(使用其他工具,效率不如其他一些答案)。

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *输出当前目录的所有文件。该for循环遍历每个文件名,并打印到标准输出。

另外,如果在目录中查找目录,则将其放入for循环中:

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d 检查文件是否为目录。


0

接受的答案将不会返回带有的文件前缀。为此使用

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done

-1

在Linux版本上,我可以使用(x86_64 GNU / Linux)进行以下工作:

for entry in "$search_dir"/*
do
  echo "$entry"
done

-1:这与接受的答案相同,除了不引用变量。不加引号$search_dir意味着如果目录名称中有空格,它将中断。(在这种情况下,您可以不引用$entry而不用引用,但是引用它会是更好的做法。)
Soren Bjornstad

你错了。我的解决方案与接受的答案相似,但并不相同。并没有错,它可以工作。这是我在包括Mac OS在内的不同unix系统上尝试过的方法,并且在每个系统上都可以使用。即使名称中带有前导空格的文件也将正确显示。search_dir =。用于输入$ search_dir / *; 回应$ entry; 完成./ aa aaa.txt ./add ./apm_tables.txt如果可以证明解决方案无效或产生错误的结果,则可以否决解决方案。
Andrushenko亚历山大

如果您更仔细地阅读我的评论,您会发现问题出在目录名而不是文件名中。这与发问者遇到的问题不同,但这仍然意味着代码可能会意外中断!这是一个无效的示例:mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done。循环运行两次,一次打印x一次y/*,第二次运行,这可能不是预期的结果。
Soren Bjornstad

而且我认为未正确引用的变量存在危险,这是错误的理由,这是不赞成投票的理由。考虑以下代码,以在保留文件的同时删除搜索目录的所有子目录:search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done。如果x当前目录中还有一个目录,则该目录将被删除,只是因为您没有引用变量!
Soren Bjornstad '19

好吧,您总是可以采用为一个任务创建的代码,略微更改任务,代码就会出错。对于所提问题,我提出的解决方案有效。但是...作为开发人员,我必须同意您的观点:我们可以使代码更健壮和通用,我们应该这样做。因此,我将添加qoutes。这将使我的答案与上面给出的答案相同,但是出于讨论的考虑(这可能对某人有用),我也保留了答案。
Andrushenko亚历山大

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.