【问题标题】:using "ls" and preserving the spaces in the resulting array使用“ls”并保留结果数组中的空格
【发布时间】:2019-07-05 21:07:54
【问题描述】:

我正在尝试使用“ls”读取目录并对其进行操作

目录示例:

$ ls -1
x x
y y
z z

脚本文件:myScript.sh

#!/bin/bash
files=(`ls -1`); 
for ((i=0; i<"${#files[@]}"; i+=1 )); do
    echo "${files[$i]}"
done

然而,输出是

$ myScript.sh
x
x
y
y
z
z

但是,如果我按以下方式定义“文件”

$ files=("x x" "y y" "z z")
$ for ((i=0; i<"${#files[@]}"; i+=1 )); do echo "${files[$i]}"; done
x x
y y
z z

如何保留“files=(`ls -1`)”中的空格?

【问题讨论】:

  • for filename in *; do echo "$filename"; done?还是array=( * ); for ((i = 0; i &lt; ${#array[@]}; i++)); do echo "${array[i]}"; done从不使用for i in $(ls anything),参见Bash Pitfalls #1
  • 好的,除了在 "${array[$i]}" 中添加 $ 之外,2 中的两个示例都有效。我仍然不明白为什么可以使用“ls”来明确区分文件名,但是操作 (ls -1) 会破坏它,但编写“*”可以正常工作。无论如何,谢谢您的建议,这比我最终找到的解决方案要好。
  • @DavidC.Rankin,...错误的文件名不仅来自随机的 dingbat,还来自错误。你听说过我关于 Python 库中缓冲区溢出导致 Python 脚本创建文件名的恐怖故事,该文件名导致维护同一目录的 shell 脚本删除 *?即使在没有操作员错误的情况下也会发生令人讨厌的事情。
  • 是的,@CharlesDuffy,这句俏皮话更多地是一种“面面相觑”的同情,而不是一个包含噩梦文件名来源的包罗万象的列表。 (我什至是一个 dingbat,有时 3-liner 坏了,并吐出我很难摆脱的文件名:) 总而言之,吸取的教训是防止最坏的情况发生,而不是寄希望于它你的盒子上不存在。

标签: arrays bash ls spaces preserve


【解决方案1】:

似乎主要结论是不要使用ls。回到 Unix 编程的更新世时代,他们使用ls;然而,这些天来,ls 被最好地限制为仅生成人类可读的显示。可以在脚本中抛出的任何东西的健壮脚本(结束行、空格、混合了希伯来语和法语的汉字,或其他),最好通过某种形式的通配符来实现(如其他人在这里BashPitfalls)。

#!/bin/bash
 for file in ./*; do
    [ -e "${file}" ] || continue
    # do some task, for example, test if it is a directory.
    if [ -d "${file}" ]; then
        echo "${file}"
    fi
done

./ 可能不是绝对必要的,但如果文件以“-”开头,它可能会有所帮助,澄清哪个文件有返回行(或行),可能还有其他一些讨厌的错误。这也是特定文件的有用模板(例如,./*.pdf)。例如,假设不知何故以下文件在您的目录中:“-t”和“&lt;CR&gt;t”。然后(揭示ls 使用非标准字符时的其他问题)

$ ls
-t  ?t
$ for file in *; do ls "${file}"; done
-t  ?t
?t

而:

$ for file in ./*; do ls "${file}"; done
./-t
./?t

还有

$ for file in ./*; do echo "${file}"; done
./-t
./
t

-- 可以实现使用 POSIX 命令的解决方法

$ for file in *; do ls -- "${file}"; done # work around
-t
?t

【讨论】:

  • 顺便说一句,如果你只想要目录,你可以遍历./*/。对于./,它对于以*s 开头的文件没有任何区别,但对于以- 开头的文件,同样的问题可以通过使用-- 作为结束来解决- of-options 参数,如 POSIX 实用程序语法指南的指南 10 中所述(pubs.opengroup.org/onlinepubs/9699919799/basedefs/…,在页面底部查找)。
  • @CharlesDuffy 我修复了文本中的“*”问题。该脚本的最初原因是我没有注意到 Windows 10 Ubuntu 上的默认掩码是 000。所以我正在清理我正在开发的一些 SW 的权限。无论如何,感谢您的耐心和建议。
【解决方案2】:

不要。

见:


如果可能,请使用 shell glob。

也就是说:

files=( * )

如果您需要将文件名表示为文本流,请使用 NUL 分隔符。

也就是说,要么:

printf '%s\0' *

find . -mindepth 1 -maxdepth 1 -print0

将发出一个以 NUL 分隔的字符串,您可以使用(在现代 bash 4.x 中)安全地将其加载到 shell 数组中:

readarray -d '' array < <(find . -mindepth 1 -maxdepth 1 -print0)

...或者,支持 bash 3.x:

array=( )
while IFS= read -r -d '' name; do
  array+=( "$name" )
done < <(find . -mindepth 1 -maxdepth 1 -print0)

在上述任何一种情况下,find 命令可能位于 FIFO、网络流或其他远程处理层的另一侧(假设存在某种复杂性阻止您使用本机 shell glob)。

【讨论】:

  • -print0 被严重低估了!
【解决方案3】:

试试这个:

eval files=($(ls -Q))

选项-Q 启用引用文件名。 如果输出不是 tty,则隐含选项 -1(不需要)。

【讨论】:

  • 这只会增加files=(*)的开销
  • 这不适用于所有可能的文件名。 sp=' '; touch "${sp}${sp}${sp}"; eval files=($( ls -Q )); ls "${files[@]}" 将失败,ls: cannot access ' ': No such file or directory,给你一个具体的例子。
  • ...这是一个无害的失败。 touch '$(touch evil)',然后运行eval files=($( ls -Q )),你就有了一个名为evil 的文件——但如果其他人想出了那个文件名,你可能会有更恶意的东西。
  • 很抱歉,您在这里被否决了,但请,请,在有替代品时不要使用eval
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-10
相关资源
最近更新 更多