我正在尝试使用 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。
search_dir=/the/path/to/base/dir/
for entry in "$search_dir"/*
do
echo "$entry"
done
这是一种让我更容易理解语法的方法:
yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
echo $eachfile
done
./
是当前工作目录,但可以替换为任何路径
*.txt
返回anything.txt
您可以通过直接在终端中输入 ls
命令轻松查看将列出的内容。
基本上,您创建一个变量 yourfilenames
,其中包含 list 命令作为单独元素返回的所有内容,然后循环遍历它。循环创建了一个临时变量 eachfile
,其中包含它正在循环的变量的单个元素,在本例中为文件名。这不一定比其他答案更好,但我觉得它很直观,因为我已经熟悉 ls
命令和 for 循环语法。
A
、B
和 C
的目录。您创建名为 B\nC
和 D
的文件,然后选择删除它们。不处理此权限的软件可能会结束即使您没有这样做的权限,也要删除预先存在的文件 B 和 C。)
这里的其他答案很好,可以回答你的问题,但这是“bash 获取目录中的文件列表”的最高谷歌结果,(我正在寻找保存文件列表)所以我想我会发布一个回答这个问题:
ls $search_path > filename.txt
如果您只想要某种类型(例如任何 .txt 文件):
ls $search_path | grep *.txt > filename.txt
注意 $search_path 是可选的; ls > filename.txt 将执行当前目录。
ls
的输出来解析文件名吗?以前没听说过这个。
for entry in "$search_dir"/* "$work_dir"/*
do
if [ -f "$entry" ];then
echo "$entry"
fi
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' : 从结果列表中删除前面的 ./
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"
xargs -0 -i echo "{}"
命令,愿意解释一下吗?特别是 -i echo "{}"
部分是做什么的?此外,我从 man
页面读到 -i
现在已弃用,我们应该使用 -I
insted。
-i
将 {}
替换为 arg。
{}
是由 find
命令替换为匹配项的字符串。
xargs
?默认情况下,find
打印它找到的内容...您可以从 -print0
中删除所有内容。
接受的答案不会返回带有 .为此使用
for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
echo "$entry"
done
如何在shell脚本中获取目录中的文件列表?
除了 most-upvoted answer by @Ignacio Vazquez-Abrams 之外,请考虑以下解决方案,它们也都有效,具体取决于您要执行的操作。请注意,您可以将 "path/to/some/dir"
替换为 .
以便在当前目录中搜索。
1. 使用 find 和 ls 列出不同类型的文件
参考:
对于查找,请参阅此答案。另请参阅我的评论。对于 ls,请参阅 linuxhandbook.com:如何在 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
以排除当前目录 .
,否则它将在目录列表的顶部打印为 .
。见这里:How to exclude this / current / dot folder from find "type 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
(内部字段分隔符——参见 The Meaning of IFS
in Bash Scripting)变量来解决。为此,我建议使用 mapfile
命令。
名为 shellscript
的 bash 脚本静态代码分析器工具建议您在要将字符串读入 bash 数组时使用 mapfile
或 read -r
,根据换行符 (\n
) 分隔元素。请参阅:https://github.com/koalaman/shellcheck/wiki/SC2206。
更新:要查看如何使用 mapfile
和 read -r
执行此操作的示例,请在此处查看我的答案:How to read a multi-line string into a regular bash "indexed" array。 我现在更喜欢使用 read -r
而不是 mapfile
,因为 mapfile
将保留任何空行作为数组中的元素(如果存在),这是我不想要的,而 read -r
[再次,我的现在的偏好] 不会将空行保留为数组中的元素。
(回到我原来的答案:)
以下是如何使用 mapfile
命令将换行符分隔的字符串转换为常规 bash“索引”数组。
# 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)"
笔记:
我们使用 ls -1 (这是一个“破折号 numeric_one”)将每个文件名放在自己的行上,从而将它们全部用换行符 \n 字符分隔。如果你想用谷歌搜索它,<<< 在 bash 中被称为“这里的字符串”。请参阅 mapfile --help 或帮助 mapfile 以获取帮助。
完整代码示例:
从我的 eRCaGuy_hello_world 存储库中的文件 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 存储库的 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
这是在目录中列出文件的另一种方式(使用不同的工具,效率不如其他一些答案)。
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
检查文件是否为目录。
for [ z
是语法错误。 if [ test
是愚蠢和错误的。不引用 "$z"
是引用错误。
类似于 Accepted answer - 但仅列出文件名而不是完整路径:
这似乎已经回答了一段时间,但我想我也想提供一个答案,只列出所需目录中的文件,而不是完整路径。
#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 的更多信息,请参见here。
有关在 shell 中查找子字符串的更多信息,请参阅 here。
ls $search_path ./* |grep ".txt"|
while IFS= read -r line
do
echo "$line"
done
for entry in "$search_dir/*"
不起作用吗?为什么我们需要将/*
放在引号之外?for entry in "${search_dir}/*"
来实现