"ls -1 path"中的-1是什么意思?

What does -1 in "ls -1 path" mean?

我正在查看一些 shell 代码,这些代码旨在获取目录中文件的数量。上面写着:

COUNT=$(ls -1 ${DIRNAME} | wc -l)

-1 部分是什么意思?我在任何其他问题中找不到任何关于此的信息,只是传递对迭代目录中文件的引用,这不是我正在查看的目录。另外,从命令中删除它似乎没有效果。

COUNT=$(ls -1 ${DIRNAME} | wc -l)

...是计算目录中文件数量的错误方法:ls -1 告诉 ls 不要将多个文件放在一行;确保 wc -l 然后会通过计算行数来计算文件数。

现在,让我们谈谈 "buggy":

  • 文件名可以包含文字换行符。 ls 的一个版本如何处理这个是实现定义的;某些版本可能会重复计算此类文件(GNU 系统不会,但我不想打赌 busybox 在嵌入式路由器上浮动的随机版本)。
  • ${DIRNAME} 的不加引号扩展允许目录名称在传递给 ls 之前进行字符串拆分和全局扩展,因此如果名称包含空格,它可以成为多个参数。这应该是 "$DIRNAME""${DIRNAME}"

...此外,这是低效的,因为它调用多个外部工具(lswc)来做一些 shell 可以在内部管理的事情。


如果您想要更强大的功能,此版本将适用于所有 POSIX shells:

count_entries() { set -- "${1:-.}"/*; if [ -e "" ]; then echo "$#"; else echo 0; fi; }
count=$(count_entries "$DIRNAME") ## ideally, DIRNAME should be lower-case.

...或者,如果您希望它执行得更快(不需要 subshell),请参阅以下内容(仅针对 bash):

# like above, but write to a named variable, not stdout
count_entries_to_var() {
  local destvar=
  set -- "${2:-.}"/*
  if [[ -e "" || -L "" ]]; then
    printf -v "$destvar" %d "$#"
  else
    printf -v "$destvar" %d 0
  fi
}
count_entries_to_var count "$DIRNAME"

...或者,如果您的目标是 bash 并且不想使用函数,您可以使用数组:

files=( "$DIRNAME"/* )
if [[ -e "${files[0]}" || -L "${files[0]}" ]]; then
  echo "At least one file exists in $DIRNAME"
  echo "...in fact, there are exactly ${#files[@]} files in $DIRNAME"
else
  echo "No files exist in $DIRNAME"
fi

最后 -- 如果你想处理一个文件名列表太大而无法放入内存,并且你有 GNU find,请考虑使用它:

find "$DIRNAME" -mindepth 1 -maxdepth 1 -printf '\n' | wc -l

...这完全避免了将名称放入流中(因此生成一个流,如果选择的话,可以简单地以字节为单位测量长度而不是行数)。

补充:

有一个边缘情况他的回答没有涵盖:如果第一个目录条目恰好是一个 broken symlink,测试使用 -e 的 glob 扩展是不够的,因为 Bash 总是将存在性测试应用于 symlink 的 target - 在这种情况下损坏的 symlink 根据定义不存在。换句话说:对于损坏的 symlink,-e 将指示 false,即使 link 本身 存在。因此,一个完全健壮的解决方案必须使用类似 [[ -e "" || -L "" ]]
的东西 (-L 测试它的参数是否是符号 link,是否损坏。)

这里有一个稍微 更短的 bash 替代方案(使用子 shell):

count=$(shopt -s nullglob; entries=(*); echo "${#entries[@]}")
  • shopt -s nullglob 确保模式在没有匹配项时扩展为空字符串。
  • entries=(*) 收集数组中的所有匹配项(在当前目录中)
  • echo "${#entries[@]}" 输出元素数组计数。
  • 由于不涉及外部实用程序,此命令不受 getconf ARG_MAX 限制,因此应该适用于大目录。

请注意,以上是否计算 隐藏 (.*) 个项目也取决于 dotglob 选项的状态。 但是,很容易在命令中构建固定的隐藏项目包含或不包含逻辑:

显式包含隐藏项:

count=$(shopt -s nullglob dotglob; entries=(*); echo "${#entries[@]}")

显式排除 隐藏项:

count=$(shopt -s nullglob; shopt -u dotglob; entries=(*); echo "${#entries[@]}")

可以将以上所有内容包装在一个灵活的函数中:

countEntries [<dir>] ... counts based on current state of the `dotglob` option
countEntries <dir> 0 ... counts non-hidden entries only
countEntries <dir> 1 ... counts all entries, including hidden ones
#!/usr/bin/env bash

# SYNOPSIS
#   countEntries [<dir> [<includeHidden>]]
# DESCRIPTION
#  <dir> defaults to .
#  <includeHidden> default to the current state of `shopt dotglob`;
#  a value of 0 explicitly EXcludes, 1 explicity INcludes hidden items.
countEntries() ( # Run entire function in subhell.
  local dir=${1:-.} includeHidden= entries
  shopt -s nullglob
  case $includeHidden in
    0) # EXclude hidden entries
      shopt -u dotglob
      ;;
    1) # INclude hidden entries
      shopt -s dotglob
      ;;
    # Otherwise: use *current state* of `dotglob`
  esac  
  entries=(""/*) # Collect in array
  echo "${#entries[@]}" # Output count.
)