如何在shell脚本中获得目录中的文件列表?

我试图获得一个目录的内容使用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。

865150 次浏览
search_dir=/the/path/to/base/dir/
for entry in "$search_dir"/*
do
echo "$entry"
done
for entry in "$search_dir"/* "$work_dir"/*
do
if [ -f "$entry" ];then
echo "$entry"
fi
done
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

在这里的其他答案是伟大的,回答你的问题,但这是顶级谷歌结果“bash获得目录中的文件列表”,(这是我正在寻找保存文件列表),所以我想我会张贴一个答案的问题:

ls $search_path > filename.txt

如果你只想要一个特定的类型(例如任何。txt文件):

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

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

这是一种方法,它的语法对我来说更容易理解:

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

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

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

下面是列出目录内文件的另一种方法(使用不同的工具,不如其他一些答案有效)。

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

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

此外,如果查找目录内的目录,则将其放置在for循环中:

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

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

接受的答案将不返回文件前缀与a。 为此使用

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
echo "$entry"
done
$ 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':从结果列表中删除前置的./
ls $search_path ./* |grep ".txt"|
while IFS= read -r line
do
echo "$line"
done

如何在shell脚本中获得目录中的文件列表?

除了得到最多赞的回答来自@Ignacio Vazquez-Abrams之外,考虑以下解决方案,它们也都有效,这取决于你想要做什么。注意,你可以用.替换"path/to/some/dir"来搜索在当前目录。

1. 使用findls列出不同类型的文件

引用:

  1. 关于find,请参见这个答案。另见我的评论是
  2. 关于ls,请参见如何在Linux中只列出目录

对于下面的任何find示例,如果你想对其排序,可以将输出管道输送到sort -V

例子:

find . -maxdepth 1 -type f | sort -V

只列出常规文件 (-type f) 1级深:

# General form
find "path/to/some/dir" -maxdepth 1 -type f


# In current directory
find . -maxdepth 1 -type f

仅列出符号链接 (-type l) 1级深:

# General form
find "path/to/some/dir" -maxdepth 1 -type l


# In current directory
find . -maxdepth 1 -type l

只列出目录 (-type d) 1级深:

注意,对于这里的find示例,我们还添加了-mindepth 1,以排除当前目录.,否则将在目录列表顶部打印为.。看这里:如何排除此/ current / dot文件夹查找"类型d"

# General form
find "path/to/some/dir" -mindepth 1 -maxdepth 1 -type d


# In current directory
find . -mindepth 1 -maxdepth 1 -type d


# OR, using `ls`:
ls -d

结合上面的一些:只列出常规文件符号链接 (-type f,l) 1级深:

使用逗号(,)分隔-type的参数:

# General form
find "path/to/some/dir" -maxdepth 1 -type f,l


# In current directory
find . -maxdepth 1 -type f,l

2. 将任何命令的输出捕获到bash索引数组中,元素以换行字符(\n)分隔

然而,$search_dir包含许多名称中带有空格的文件。在这种情况下,该脚本不能按预期运行。

这是通过告诉bash根据换行字符\n而不是空格字符分隔字符串中的元素来解决的——空格字符是bash使用的默认IFS(内部字段分隔符——见IFS在Bash脚本中的意义)变量。要做到这一点,我建议使用mapfile命令。

名为shellscript的bash脚本静态代码分析工具建议在您想将字符串读入bash数组时使用mapfileread -r,并根据换行字符(\n)分隔元素。看到:https://github.com/koalaman/shellcheck/wiki/SC2206

更新:要查看如何使用mapfileread -r做到这一点的例子,请参阅我的回答:如何将多行字符串读入常规bash "索引"数组我现在更喜欢使用read -r而不是__ABC0,因为__ABC0将保留数组中的任何空行作为元素,如果存在的话,这是我不想要的,而read -r[再次,我现在的偏好]将不保留数组中的空行作为元素。

(回到我最初的答案:)

下面是如何将换行分隔的字符串转换为常规的bash "索引"数组的mapfile命令

# Capture the output of `ls -1` into a regular bash "indexed" array.
# - includes both files AND directories!
mapfile -t allfilenames_array <<< "$(ls -1)"
# Capture the output of `find` into a regular bash "indexed" array
# - includes directories ONLY!
# Note: for other `-type` options, see `man find`.
mapfile -t dirnames_array \
<<< "$(find . -mindepth 1 -maxdepth 1 -type d | sort -V)"

注:

  1. 我们使用ls -1(那是一个&;破折号numeral_one")来把每个文件名放在自己的行上,从而用换行符\n字符将它们分开。
  2. 如果你想谷歌它,<<<被称为一个"here字符串"在bash。
  3. 请参见mapfile --helphelp mapfile

完整的代码示例:

从我的eRCaGuy_hello_world repo中的array_list_all_files_and_directories.sh文件:

echo "Output of 'ls -1'"
echo "-----------------"
ls -1
echo ""


# Capture the output of `ls -1` into a regular bash "indexed" array.
# - includes both files AND directories!
mapfile -t allfilenames_array <<< "$(ls -1)"
# Capture the output of `find` into a regular bash "indexed" array
# - includes directories ONLY!
# Note: for other `-type` options, see `man find` and see my answer here:
# https://stackoverflow.com/a/71345102/4561887
mapfile -t dirnames_array \
<<< "$(find . -mindepth 1 -maxdepth 1 -type d | sort -V)"


# Get the number of elements in each array
allfilenames_array_len="${#allfilenames_array[@]}"
dirnames_array_len="${#dirnames_array[@]}"


# 1. Now manually print all elements in each array


echo "All filenames (files AND dirs) (count = $allfilenames_array_len):"
for filename in "${allfilenames_array[@]}"; do
echo "    $filename"
done
echo "Dirnames ONLY (count = $dirnames_array_len):"
for dirname in "${dirnames_array[@]}"; do
# remove the `./` from the beginning of each dirname
dirname="$(basename "$dirname")"
echo "    $dirname"
done
echo ""


# OR, 2. manually print the index number followed by all elements in the array


echo "All filenames (files AND dirs) (count = $allfilenames_array_len):"
for i in "${!allfilenames_array[@]}"; do
printf "  %3i: %s\n" "$i" "${allfilenames_array["$i"]}"
done
echo "Dirnames ONLY (count = $dirnames_array_len):"
for i in "${!dirnames_array[@]}"; do
# remove the `./` from the beginning of each dirname
dirname="$(basename "${dirnames_array["$i"]}")"
printf "  %3i: %s\n" "$i" "$dirname"
done
echo ""

下面是上面代码块的示例输出,在我的eRCaGuy_hello_world repo的eRCaGuy_hello_world / python目录中运行:

eRCaGuy_hello_world/python$ ../bash/array_list_all_files_and_directories.sh
Output of 'ls -1'
-----------------
autogenerate_c_or_cpp_code.py
autogenerated
auto_white_balance_img.py
enum_practice.py
raw_bytes_practice.py
slots_practice
socket_talk_to_ethernet_device.py
textwrap_practice_1.py
yaml_import


All filenames (files AND dirs) (count = 9):
autogenerate_c_or_cpp_code.py
autogenerated
auto_white_balance_img.py
enum_practice.py
raw_bytes_practice.py
slots_practice
socket_talk_to_ethernet_device.py
textwrap_practice_1.py
yaml_import
Dirnames ONLY (count = 3):
autogenerated
slots_practice
yaml_import


All filenames (files AND dirs) (count = 9):
0: autogenerate_c_or_cpp_code.py
1: autogenerated
2: auto_white_balance_img.py
3: enum_practice.py
4: raw_bytes_practice.py
5: slots_practice
6: socket_talk_to_ethernet_device.py
7: textwrap_practice_1.py
8: yaml_import
Dirnames ONLY (count = 3):
0: autogenerated
1: slots_practice
2: yaml_import


类似于接受的答案-但只列出文件名而不是完整的路径:

这个问题似乎已经回答了一段时间,但我想我还想提供一个答案,只列出所需目录中的文件,而不是完整的路径。

    #search_dir=/the/path/to/base/dir/
IFS=$'\n' #for in $() splits based on IFS
search_dir="$(pwd)"
for entry in $(ls $search_dir)
do
echo $entry
done

如果你还想特定文件的过滤器,你可以添加一个grep -q语句。

    #search_dir=/the/path/to/base/dir/
IFS=$'\n' #for in $() splits based on IFS
search_dir="$(pwd)"
for entry in $(ls $search_dir)
do
if grep -q "File should contain this entire string" <<< $entry; then
echo "$entry"
fi
done

引用:

更多关于IFS的信息可以找到在这里

关于在shell中查找子字符串的更多信息可以找到在这里