我试图获得一个目录的内容使用shell脚本。

我的剧本是:

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

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

我知道我可以在*中使用for条目,但这只适用于我的当前目录。

我知道我可以更改到该目录,使用*的入口,然后更改回来,但我的特殊情况阻止了我这样做。

我有两个相对路径$search_dir和$work_dir,我必须同时工作,读取它们,创建/删除它们中的文件等。

现在我该怎么办?

PS:我使用bash。


当前回答

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

其他回答

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

除了伊格纳西奥·巴斯克斯-艾布拉姆斯得到最多赞的答案,考虑一下下面的解决方案,它们也都有效,这取决于你想做什么。注意,您可以将“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

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

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

# 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数组时使用mapfile或read -r,并根据换行字符(\n)分隔元素。参见:https://github.com/koalaman/shellcheck/wiki/SC2206。

更新:要查看如何使用mapfile和read -r做到这一点的示例,请参阅我的回答:如何将多行字符串读入常规bash“索引”数组。我现在更喜欢使用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(这是一个“破折号numeral_one”),以便将每个文件名放在自己的行上,从而通过换行\n字符将它们分开。 如果你想谷歌它,<<<在bash中被称为“here字符串”。 请参阅mapfile——help或help 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

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

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

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检查文件是否为目录。

所接受的答案将不会返回带有'。'的文件前缀。 要做到这一点

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