dcw*_*ers 4 shell bash array brace-expansion shellcheck
在跟踪我的 shellscript 中的错误时,我在此代码片段中发现了以下行为:
declare -a filelist
readarray filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
sha256sum ${filelist[$file]} | head -c 64
done
Run Code Online (Sandbox Code Playgroud)
当数组filelist不在双引号中时,命令成功。我一直在使用 ShellCheck 来尝试改进我的编码,它建议-
双引号以防止通配和分词。
在这种情况下,我不担心分词,但在很多其他情况下我很担心,所以我试图保持我的代码一致。但是,当我双引号数组时,命令失败。将代码简化为单个元素可得到以下结果:
bash-5.0# sha256sum ${filelist[0]} | head -c 64
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
bash-5.0# sha256sum "${filelist[0]}" | head -c 64
sha256sum: can't open 'file1
': No such file or directory
Run Code Online (Sandbox Code Playgroud)
我显然可以......不是双引号,因为在这种情况下分词不是问题。但我想发帖,因为将来可能会。
我的问题有两个部分:
另外,出于好奇,为什么不echo ${filelist[0]}包含额外的换行符,但是echo "${filelist[0]}"呢?
引用数组扩展绝对没有问题。
而且,当然,只要您知道并接受后果,不引用它也没有问题。任何未引用的扩展都会受到拆分和通配。而且,在你的代码中,${filelist[…]}受IFS字符去除(和分裂如果字符串包含任何<space>,<tab>或<newline>)。
这就是没有引用扩展所做的事情,删除尾随<newline>.
是什么造成这个问题是您正在使用readarray未经每个数组元素删除尾部分隔符。
这样做会保留<newline>反映在错误消息上的尾随。
你可以使用的是:
readarray -t filelist < <(ls -A)
Run Code Online (Sandbox Code Playgroud)
该-t选项将删除每个文件名的所有尾随换行符。
-t 从读取的每一行中删除尾随 delim(默认换行符)。
但是您的代码还有一些其他问题。
无需声明或清空数组filelist。它默认由 readarray 完成。它需要在其他一些情况下完成。
不需要解析 的输出ls,事实上,这是一个坏主意。获取数组中文件列表的最简单方法很简单:
filelist=( ./* )
Run Code Online (Sandbox Code Playgroud)
而且,为了让它更好,避免目录是一个好主意:
for file in ./*; do
[[ -f $file ]] && filelist+=( "$file" )
done
Run Code Online (Sandbox Code Playgroud)在循环中,$file应该使用var 的值:
for file in "${filelist[@]}"; do
sha256sum "$file" | head -c 64
done
Run Code Online (Sandbox Code Playgroud)
除非您使用for file in "${!filelist[@]}"; dowhich 将列出数组的键。
整个名单可以只处理一个呼叫sha256sum:
sha256sum "${filelist[@]}" | cut -c -64
Run Code Online (Sandbox Code Playgroud)改进后的脚本是:
filelist=() # declare filelist as an array and empty it.
for file in ./*; do
if [[ -f $file ]]; then
filelist+=( "$file" )
fi
done
declare -r filelist # declare filelist as readonly.
sha256sum "${filelist[@]}" | cut -c -64
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我不担心分词
好吧,事实上,您依赖它从数组条目中删除尾随的换行符!
Bash 的readarray( mapfile)默认保留分隔符。手册页或命令行帮助似乎没有明确说明,但有一个选项可以删除分隔符,因此默认情况下它不会被删除:
-t Remove a trailing delim (default newline) from each line read.
Run Code Online (Sandbox Code Playgroud)
因此,数组中的实际字符串是.file1[newline]
没有引号,分词会删除尾随空格,修复换行符。但是,如果您的文件名中包含空格,那么像往常一样,分词会将它们弄乱。双引号数组可以防止这种情况。要回答您的第一个问题,最佳做法是双引号,这里我们只有一个不需要的额外换行符。
(双引号数组或者$@是稍微令人困惑的例外情况,其中双引号字符串导致多个单词,每个单词对应一个数组元素。)
您也可以${filelist[$file]}在sha256sum命令行中使用。这行不通,file已经包含从数组接收到的值,而不是索引。
作为最小的修改,这可能有效:
declare -a filelist
readarray -t filelist < <(ls -A)
readonly filelist
for file in "${filelist[@]}"; do
sha256sum "$file" | head -c 64
done
Run Code Online (Sandbox Code Playgroud)
(我也不认为显式declare实际上是必要的。)
上述问题与ls本身无关。如果您将文件名存储在文件中,每行一个,并使用readarray/mapfile读取它们而不使用该-t选项,您会遇到同样的问题。(或者,如果您阅读了 的输出find,但在这种情况下,您也许可以find -exec改用。)
当然,这是一个无用的用法,ls并且某些版本ls可能会在输出时破坏您的文件名。(我认为 GNU ls 在输出到管道时不会这样做。)
在 Bash 中,您可以改为使用 glob 填充数组:
shopt -s dotglob
filelist=(*)
for file in *; do ...
Run Code Online (Sandbox Code Playgroud)
或者只是在 glob 上运行循环而不存储到数组:
shopt -s dotglob
for file in *; do ...
Run Code Online (Sandbox Code Playgroud)
请注意,你需要shopt -s dotglob得到*匹配点文件,这是壳牌依赖。
| 归档时间: |
|
| 查看次数: |
923 次 |
| 最近记录: |